/*
HTTP Simple Queue Service - httpsqs v1.7
Author: Zhang Yan (http://blog.s135.com), E-mail: net@s135.com
This is free software, and you are welcome to modify and redistribute it under the New BSD License
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <pthread.h>
#include <err.h>
#include <event.h>
#include <evhttp.h>
#include <tcbdb.h>
#include "prename.h"
#define VERSION "1.7"
/* 每个队列的默认最大长度为100万条 */
#define HTTPSQS_DEFAULT_MAXQUEUE 1000000
/* 全局设置 */
TCBDB *httpsqs_db_tcbdb; /* 数据表 */
int httpsqs_settings_syncinterval; /* 同步更新内容到磁盘的间隔时间 */
char *httpsqs_settings_pidfile; /* PID文件 */
char *httpsqs_settings_auth; /* 验证密码 */
/* 创建多层目录的函数 */
void create_multilayer_dir( char *muldir )
{
int i,len;
char str[512];
strncpy( str, muldir, 512 );
len=strlen(str);
for( i=0; i<len; i++ )
{
if( str[i]=='/' )
{
str[i] = ' ';
//判断此目录是否存在,不存在则创建
if( access(str, F_OK)!=0 )
{
mkdir( str, 0777 );
}
str[i]='/';
}
}
if( len>0 && access(str, F_OK)!=0 )
{
mkdir( str, 0777 );
}
return;
}
char *urldecode(char *input_str)
{
int len = strlen(input_str);
char *str = strdup(input_str);
char *dest = str;
char *data = str;
int value;
int c;
while (len--) {
//if (*data == '+') {
// *dest = ' ';
//}
//else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2)))
{
c = ((unsigned char *)(data+1))[0];
if (isupper(c))
c = tolower(c);
value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
c = ((unsigned char *)(data+1))[1];
if (isupper(c))
c = tolower(c);
value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
*dest = (char)value ;
data += 2;
len -= 2;
} else {
*dest = *data;
}
data++;
dest++;
}
*dest = ' ';
return str;
}
/* 读取队列写入点的值 */
static int httpsqs_read_putpos(const char* httpsqs_input_name)
{
int queue_value = 0;
char *queue_value_tmp;
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%s", httpsqs_input_name, "putpos");
queue_value_tmp = tcbdbget2(httpsqs_db_tcbdb, queue_name);
if(queue_value_tmp){
queue_value = atoi(queue_value_tmp);
free(queue_value_tmp);
}
return queue_value;
}
/* 读取队列读取点的值 */
static int httpsqs_read_getpos(const char* httpsqs_input_name)
{
int queue_value = 0;
char *queue_value_tmp;
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%s", httpsqs_input_name, "getpos");
queue_value_tmp = tcbdbget2(httpsqs_db_tcbdb, queue_name);
if(queue_value_tmp){
queue_value = atoi(queue_value_tmp);
free(queue_value_tmp);
}
return queue_value;
}
/* 读取用于设置的最大队列数 */
static int httpsqs_read_maxqueue(const char* httpsqs_input_name)
{
int queue_value = 0;
char *queue_value_tmp;
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%s", httpsqs_input_name, "maxqueue");
queue_value_tmp = tcbdbget2(httpsqs_db_tcbdb, queue_name);
if(queue_value_tmp){
queue_value = atoi(queue_value_tmp);
free(queue_value_tmp);
} else {
queue_value = HTTPSQS_DEFAULT_MAXQUEUE; /* 默认队列长度 */
}
return queue_value;
}
/* 设置最大的队列数量,返回值为设置的队列数量。如果返回值为0,则表示设置取消(取消原因为:设置的最大的队列数量小于”当前队列写入位置点“和”当前队列读取位置点“,或者”当前队列写入位置点“小于”当前队列的读取位置点) */
static int httpsqs_maxqueue(const char* httpsqs_input_name, int httpsqs_input_num)
{
int queue_put_value = 0;
int queue_get_value = 0;
int queue_maxnum_int = 0;
/* 读取当前队列写入位置点 */
queue_put_value = httpsqs_read_putpos(httpsqs_input_name);
/* 读取当前队列读取位置点 */
queue_get_value = httpsqs_read_getpos(httpsqs_input_name);
/* 设置最大的队列数量,最小值为10条,最大值为10亿条 */
queue_maxnum_int = httpsqs_input_num;
/* 设置的最大的队列数量必须大于等于”当前队列写入位置点“和”当前队列读取位置点“,并且”当前队列写入位置点“必须大于等于”当前队列读取位置点“ */
if (queue_maxnum_int >= queue_put_value && queue_maxnum_int >= queue_get_value && queue_put_value >= queue_get_value) {
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
char queue_maxnum[16] = {0};
sprintf(queue_name, "%s:%s", httpsqs_input_name, "maxqueue");
sprintf(queue_maxnum, "%d", queue_maxnum_int);
tcbdbput2(httpsqs_db_tcbdb, queue_name, queue_maxnum);
tcbdbsync(httpsqs_db_tcbdb); /* 实时刷新到磁盘 */
return queue_maxnum_int;
}
return 0;
}
/* 重置队列,0表示重置成功 */
static int httpsqs_reset(const char* httpsqs_input_name)
{
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%s", httpsqs_input_name, "putpos");
tcbdbout2(httpsqs_db_tcbdb, queue_name);
memset(queue_name, ' ', 300);
sprintf(queue_name, "%s:%s", httpsqs_input_name, "getpos");
tcbdbout2(httpsqs_db_tcbdb, queue_name);
memset(queue_name, ' ', 300);
sprintf(queue_name, "%s:%s", httpsqs_input_name, "maxqueue");
tcbdbout2(httpsqs_db_tcbdb, queue_name);
tcbdbsync(httpsqs_db_tcbdb); /* 实时刷新到磁盘 */
return 0;
}
/* 查看单条队列内容 */
char *httpsqs_view(const char* httpsqs_input_name, int pos)
{
char *queue_value;
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%d", httpsqs_input_name, pos);
queue_value = tcbdbget2(httpsqs_db_tcbdb, queue_name);
return queue_value;
}
/* 修改定时更新内存内容到磁盘的间隔时间,返回间隔时间(秒) */
static int httpsqs_synctime(int httpsqs_input_num)
{
if (httpsqs_input_num >= 1) {
httpsqs_settings_syncinterval = httpsqs_input_num;
}
return httpsqs_settings_syncinterval;
}
/* 获取本次“入队列”操作的队列写入点 */
static int httpsqs_now_putpos(const char* httpsqs_input_name)
{
int maxqueue_num = 0;
int queue_put_value = 0;
int queue_get_value = 0;
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
char queue_input[32] = {0};
/* 获取最大队列数量 */
maxqueue_num = httpsqs_read_maxqueue(httpsqs_input_name);
/* 读取当前队列写入位置点 */
queue_put_value = httpsqs_read_putpos(httpsqs_input_name);
/* 读取当前队列读取位置点 */
queue_get_value = httpsqs_read_getpos(httpsqs_input_name);
sprintf(queue_name, "%s:%s", httpsqs_input_name, "putpos");
/* 队列写入位置点加1 */
queue_put_value = queue_put_value + 1;
if (queue_put_value == queue_get_value) { /* 如果队列写入ID+1之后追上队列读取ID,则说明队列已满,返回0,拒绝继续写入 */
queue_put_value = 0;
}
else if (queue_get_value <= 1 && queue_put_value > maxqueue_num) { /* 如果队列写入ID大于最大队列数量,并且从未进行过出队列操作(=0)或进行过1次出队列操作(=1),返回0,拒绝继续写入 */
queue_put_value = 0;
}
else if (queue_put_value > maxqueue_num) { /* 如果队列写入ID大于最大队列数量,则重置队列写入位置点的值为1 */
if(tcbdbput2(httpsqs_db_tcbdb, queue_name, "1")) {
queue_put_value = 1;
}
} else { /* 队列写入位置点加1后的值,回写入数据库 */
sprintf(queue_input, "%d", queue_put_value);
tcbdbput2(httpsqs_db_tcbdb, queue_name, (char *)queue_input);
}
return queue_put_value;
}
/* 获取本次“出队列”操作的队列读取点,返回值为0时队列全部读取完成 */
static int httpsqs_now_getpos(const char* httpsqs_input_name)
{
int maxqueue_num = 0;
int queue_put_value = 0;
int queue_get_value = 0;
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
/* 获取最大队列数量 */
maxqueue_num = httpsqs_read_maxqueue(httpsqs_input_name);
/* 读取当前队列写入位置点 */
queue_put_value = httpsqs_read_putpos(httpsqs_input_name);
/* 读取当前队列读取位置点 */
queue_get_value = httpsqs_read_getpos(httpsqs_input_name);
/* 如果queue_get_value的值不存在,重置队列读取位置点为1 */
sprintf(queue_name, "%s:%s", httpsqs_input_name, "getpos");
/* 如果queue_get_value的值不存在,重置为1 */
if (queue_get_value == 0 && queue_put_value > 0) {
queue_get_value = 1;
tcbdbput2(httpsqs_db_tcbdb, queue_name, "1");
/* 如果队列的读取值(出队列)小于队列的写入值(入队列) */
} else if (queue_get_value < queue_put_value) {
queue_get_value = queue_get_value + 1;
char queue_input[32] = {0};
sprintf(queue_input, "%d", queue_get_value);
tcbdbput2(httpsqs_db_tcbdb, queue_name, queue_input);
/* 如果队列的读取值(出队列)大于队列的写入值(入队列),并且队列的读取值(出队列)小于最大队列数量 */
} else if (queue_get_value > queue_put_value && queue_get_value < maxqueue_num) {
queue_get_value = queue_get_value + 1;
char queue_input[32] = {0};
sprintf(queue_input, "%d", queue_get_value);
tcbdbput2(httpsqs_db_tcbdb, queue_name, queue_input);
/* 如果队列的读取值(出队列)大于队列的写入值(入队列),并且队列的读取值(出队列)等于最大队列数量 */
} else if (queue_get_value > queue_put_value && queue_get_value == maxqueue_num) {
queue_get_value = 1;
tcbdbput2(httpsqs_db_tcbdb, queue_name, "1");
/* 队列的读取值(出队列)等于队列的写入值(入队列),即队列中的数据已全部读出 */
} else {
queue_get_value = 0;
}
return queue_get_value;
}
/* 处理模块 */
void httpsqs_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *buf;
buf = evbuffer_new();
/* 分析URL参数 */
const char *httpsqs_query_part;
struct evkeyvalq httpsqs_http_query;
httpsqs_query_part = evhttp_uri_get_query(req->uri_elems);
evhttp_parse_query_str(httpsqs_query_part, &httpsqs_http_query);
/* 接收GET表单参数 */
const char *httpsqs_input_auth = evhttp_find_header (&httpsqs_http_query, "auth"); /* 队列名称 */
const char *httpsqs_input_name = evhttp_find_header (&httpsqs_http_query, "name"); /* 队列名称 */
const char *httpsqs_input_charset = evhttp_find_header (&httpsqs_http_query, "charset"); /* 操作类别 */
const char *httpsqs_input_opt = evhttp_find_header (&httpsqs_http_query, "opt"); /* 操作类别 */
const char *httpsqs_input_data = evhttp_find_header (&httpsqs_http_query, "data"); /* 操作类别 */
const char *httpsqs_input_pos_tmp = evhttp_find_header (&httpsqs_http_query, "pos"); /* 队列位置点 字符型 */
const char *httpsqs_input_num_tmp = evhttp_find_header (&httpsqs_http_query, "num"); /* 队列总长度 字符型 */
int httpsqs_input_pos = 0;
int httpsqs_input_num = 0;
/* 返回给用户的Header头信息 */
if (httpsqs_input_charset != NULL && strlen(httpsqs_input_charset) <= 40) {
char content_type[64] = {0};
sprintf(content_type, "text/plain; charset=%s", httpsqs_input_charset);
evhttp_add_header(req->output_headers, "Content-Type", content_type);
} else {
evhttp_add_header(req->output_headers, "Content-Type", "text/plain");
}
evhttp_add_header(req->output_headers, "Connection", "keep-alive");
evhttp_add_header(req->output_headers, "Cache-Control", "no-cache");
//evhttp_add_header(req->output_headers, "Connection", "close");
/* 权限校验 */
bool is_auth_pass = false; /* 是否验证通过 */
if (httpsqs_settings_auth != NULL) {
/* 如果命令行启动参数设置了验证密码 */
if (httpsqs_input_auth != NULL && strcmp(httpsqs_settings_auth, httpsqs_input_auth) == 0) {
is_auth_pass = true;
} else {
is_auth_pass = false;
}
} else {
/* 如果命令行启动参数没有设置验证密码 */
is_auth_pass = true;
}
if (is_auth_pass == false) {
/* 校验失败 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_AUTH_FAILED");
}
else
{
/* 校验成功,或者命令行启动参数没有设置校验密码 */
if (httpsqs_input_pos_tmp != NULL) {
httpsqs_input_pos = atoi(httpsqs_input_pos_tmp); /* 队列位置点 数值型 */
}
if (httpsqs_input_num_tmp != NULL) {
httpsqs_input_num = atoi(httpsqs_input_num_tmp); /* 队列总长度 数值型 */
}
/*参数是否存在判断 */
if (httpsqs_input_name != NULL && httpsqs_input_opt != NULL && strlen(httpsqs_input_name) <= 256) {
/* 入队列 */
if (strcmp(httpsqs_input_opt, "put") == 0) {
/* 优先接收POST正文信息 */
/*Mod by Yang for 远鉴 2017-12-7*/
/*修改说明:1) 匹配入队列时多队列内容相同时,一次性分配到 以分隔符"_"为限定的多个httpsqs_input_name中。*/
int n=0;
char *p[20];
char *buf_temp = httpsqs_input_name;
char *ptr = NULL;
while ((p[n] = strtok_r(buf_temp,"_",&ptr)) != NULL)
{
n++;
buf_temp = NULL;
}
buf_temp = NULL;
int buffer_data_len;
buffer_data_len = EVBUFFER_LENGTH(req->input_buffer);
if (buffer_data_len > 0) {
int i,queue_put_value;
for ( i=0;i<n;i++)
{
httpsqs_input_name = p[i];
queue_put_value = httpsqs_now_putpos((char *)httpsqs_input_name);
if (queue_put_value > 0) {
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%d", httpsqs_input_name, queue_put_value);
char *httpsqs_input_postbuffer;
char *buffer_data = (char *)tccalloc(1, buffer_data_len + 1);
memcpy (buffer_data, EVBUFFER_DATA(req->input_buffer), buffer_data_len);
httpsqs_input_postbuffer = urldecode(buffer_data);
tcbdbput2(httpsqs_db_tcbdb, queue_name, httpsqs_input_postbuffer);
memset(queue_name, ' ', 300);
sprintf(queue_name, "%d", queue_put_value);
evhttp_add_header(req->output_headers, "Pos", queue_name);
//evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_OK");
free(httpsqs_input_postbuffer);
free(buffer_data);
} else {
//evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_END");
}
}
if (queue_put_value > 0)
{
evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_OK");
}
else
{
evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_END");
}
/* 如果POST正文无内容,则取URL中data参数的值 */
} else if (httpsqs_input_data != NULL) {
int k,queue_put_value;
for (k=0;k<n;k++)
{
httpsqs_input_name = p[k];
queue_put_value = httpsqs_now_putpos((char *)httpsqs_input_name);
if (queue_put_value > 0) {
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%d", httpsqs_input_name, queue_put_value);
buffer_data_len = strlen(httpsqs_input_data);
char *httpsqs_input_postbuffer;
char *buffer_data = (char *)tccalloc(1, buffer_data_len + 1);
memcpy (buffer_data, httpsqs_input_data, buffer_data_len);
httpsqs_input_postbuffer = urldecode(buffer_data);
tcbdbput2(httpsqs_db_tcbdb, queue_name, httpsqs_input_postbuffer);
memset(queue_name, ' ', 300);
sprintf(queue_name, "%d", queue_put_value);
evhttp_add_header(req->output_headers, "Pos", queue_name);
//evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_OK");
free(httpsqs_input_postbuffer);
free(buffer_data);
} else {
//evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_END");
}
}
if (queue_put_value > 0)
{
evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_OK");
}
else
{
evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_END");
}
} else {
evbuffer_add_printf(buf, "%s", "HTTPSQS_PUT_ERROR");
}
}
/* 出队列 */
else if (strcmp(httpsqs_input_opt, "get") == 0) {
int queue_get_value = 0;
queue_get_value = httpsqs_now_getpos((char *)httpsqs_input_name);
if (queue_get_value == 0) {
evbuffer_add_printf(buf, "%s", "HTTPSQS_GET_END");
} else {
char queue_name[300] = {0}; /* 队列名称的总长度,用户输入的队列长度少于256字节 */
sprintf(queue_name, "%s:%d", httpsqs_input_name, queue_get_value);
char *httpsqs_output_value;
httpsqs_output_value = tcbdbget2(httpsqs_db_tcbdb, queue_name);
if (httpsqs_output_value) {
memset(queue_name, ' ', 300);
sprintf(queue_name, "%d", queue_get_value);
evhttp_add_header(req->output_headers, "Pos", queue_name);
evbuffer_add_printf(buf, "%s", httpsqs_output_value);
free(httpsqs_output_value);
} else {
evbuffer_add_printf(buf, "%s", "HTTPSQS_GET_END");
}
}
}
/* 查看队列状态(普通浏览方式) */
else if (strcmp(httpsqs_input_opt, "status") == 0) {
int maxqueue = httpsqs_read_maxqueue((char *)httpsqs_input_name); /* 最大队列数量 */
int putpos = httpsqs_read_putpos((char *)httpsqs_input_name); /* 入队列写入位置 */
int getpos = httpsqs_read_getpos((char *)httpsqs_input_name); /* 出队列读取位置 */
int ungetnum;
const char *put_times;
const char *get_times;
if (putpos >= getpos) {
ungetnum = abs(putpos - getpos); /* 尚未出队列条数 */
put_times = "1st lap";
get_times = "1st lap";
} else if (putpos < getpos) {
ungetnum = abs(maxqueue - getpos + putpos); /* 尚未出队列条数 */
put_times = "2nd lap";
get_times = "1st lap";
}
evbuffer_add_printf(buf, "HTTP Simple Queue Service v%s
", VERSION);
evbuffer_add_printf(buf, "------------------------------
");
evbuffer_add_printf(buf, "Queue Name: %s
", httpsqs_input_name);
evbuffer_add_printf(buf, "Maximum number of queues: %d
", maxqueue);
evbuffer_add_printf(buf, "Put position of queue (%s): %d
", put_times, putpos);
evbuffer_add_printf(buf, "Get position of queue (%s): %d
", get_times, getpos);
evbuffer_add_printf(buf, "Number of unread queue: %d
", ungetnum);
}
/* 查看队列状态(JSON方式,方便客服端程序处理) */
else if (strcmp(httpsqs_input_opt, "status_json") == 0) {
int maxqueue = httpsqs_read_maxqueue((char *)httpsqs_input_name); /* 最大队列数量 */
int putpos = httpsqs_read_putpos((char *)httpsqs_input_name); /* 入队列写入位置 */
int getpos = httpsqs_read_getpos((char *)httpsqs_input_name); /* 出队列读取位置 */
int ungetnum;
const char *put_times;
const char *get_times;
if (putpos >= getpos) {
ungetnum = abs(putpos - getpos); /* 尚未出队列条数 */
put_times = "1";
get_times = "1";
} else if (putpos < getpos) {
ungetnum = abs(maxqueue - getpos + putpos); /* 尚未出队列条数 */
put_times = "2";
get_times = "1";
}
evbuffer_add_printf(buf, "{"name":"%s","maxqueue":%d,"putpos":%d,"putlap":%s,"getpos":%d,"getlap":%s,"unread":%d}
", httpsqs_input_name, maxqueue, putpos, put_times, getpos, get_times, ungetnum);
}
/* 查看单条队列内容 */
else if (strcmp(httpsqs_input_opt, "view") == 0 && httpsqs_input_pos >= 1 && httpsqs_input_pos <= 1000000000) {
char *httpsqs_output_value;
httpsqs_output_value = httpsqs_view ((char *)httpsqs_input_name, httpsqs_input_pos);
if (httpsqs_output_value) {
evbuffer_add_printf(buf, "%s", httpsqs_output_value);
free(httpsqs_output_value);
}
}
/* 重置队列 */
else if (strcmp(httpsqs_input_opt, "reset") == 0) {
int reset = httpsqs_reset((char *)httpsqs_input_name);
if (reset == 0) {
evbuffer_add_printf(buf, "%s", "HTTPSQS_RESET_OK");
} else {
evbuffer_add_printf(buf, "%s", "HTTPSQS_RESET_ERROR");
}
}
/* 设置最大的队列数量,最小值为10条,最大值为10亿条 */
else if (strcmp(httpsqs_input_opt, "maxqueue") == 0 && httpsqs_input_num >= 10 && httpsqs_input_num <= 1000000000) {
if (httpsqs_maxqueue((char *)httpsqs_input_name, httpsqs_input_num) != 0) {
/* 设置成功 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_MAXQUEUE_OK");
} else {
/* 设置取消 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_MAXQUEUE_CANCEL");
}
}
/* 设置定时更新内存内容到磁盘的间隔时间,最小值为1秒,最大值为10亿秒 */
else if (strcmp(httpsqs_input_opt, "synctime") == 0 && httpsqs_input_num >= 1 && httpsqs_input_num <= 1000000000) {
if (httpsqs_synctime(httpsqs_input_num) >= 1) {
/* 设置成功 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_SYNCTIME_OK");
} else {
/* 设置取消 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_SYNCTIME_CANCEL");
}
} else {
/* 命令错误 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_ERROR");
}
} else {
/* 命令错误 */
evbuffer_add_printf(buf, "%s", "HTTPSQS_ERROR");
}
}
/* 输出内容给客户端 */
evhttp_send_reply(req, HTTP_OK, "OK", buf);
/* 内存释放 */
evhttp_clear_headers(&httpsqs_http_query);
evbuffer_free(buf);
}
/* 子进程信号处理 */
static void kill_signal_worker(const int sig) {
/* 同步内存数据到磁盘,并关闭数据库 */
tcbdbsync(httpsqs_db_tcbdb);
tcbdbclose(httpsqs_db_tcbdb);
tcbdbdel(httpsqs_db_tcbdb);
exit(0);
}
/* 父进程信号处理 */
static void kill_signal_master(const int sig) {
/* 删除PID文件 */
remove(httpsqs_settings_pidfile);
/* 给进程组发送SIGTERM信号,结束子进程 */
kill(0, SIGTERM);
exit(0);
}
/* 定时同步线程,定时将内存中的内容写入磁盘 */
static void sync_worker(const int sig) {
pthread_detach(pthread_self());
while(1)
{
/* 间隔httpsqs_settings_syncinterval秒同步一次数据到磁盘 */
sleep(httpsqs_settings_syncinterval);
/* 同步内存数据到磁盘 */
tcbdbsync(httpsqs_db_tcbdb);
}
}
static void show_help(void)
{
char *b = "--------------------------------------------------------------------------------------------------
"
"HTTP Simple Queue Service - httpsqs v" VERSION " (April 14, 2011)
"
"Author: Zhang Yan (http://blog.s135.com), E-mail: net@s135.com
"
"This is free software, and you are welcome to modify and redistribute it under the New BSD License
"
"
"
"-l <ip_addr> interface to listen on, default is 0.0.0.0
"
"-p <num> TCP port number to listen on (default: 1218)
"
"-x <path> database directory (example: /opt/httpsqs/data)
"
"-t <second> keep-alive timeout for an http request (default: 60)
"
"-s <second> the interval to sync updated contents to the disk (default: 5)
"
"-c <num> the maximum number of non-leaf nodes to be cached (default: 1024)
"
"-m <size> database memory cache size in MB (default: 100)
"
"-i <file> save PID in <file> (default: /tmp/httpsqs.pid)
"
"-a <auth> the auth password to access httpsqs (example: mypass123)
"
"-d run as a daemon
"
"-h print this help and exit
"
"Use command "killall httpsqs", "pkill httpsqs" and "kill `cat /tmp/httpsqs.pid`" to stop httpsqs.
"
"Please note that don't use the command "pkill -9 httpsqs" and "kill -9 PID of httpsqs"!
"
"
"
"Please visit "http://code.google.com/p/httpsqs" for more help information.
"
"--------------------------------------------------------------------------------------------------
"
"
";
fprintf(stderr, b, strlen(b));
}
int main(int argc, char *argv[], char *envp[])
{
int c;
/* 默认参数设置 */
char *httpsqs_settings_listen = "0.0.0.0";
int httpsqs_settings_port = 1218;
char *httpsqs_settings_datapath = NULL;
bool httpsqs_settings_daemon = false;
int httpsqs_settings_timeout = 60; /* 单位:秒 */
httpsqs_settings_syncinterval = 5; /* 单位:秒 */
int httpsqs_settings_cachenonleaf = 1024; /* 缓存非叶子节点数。单位:条 */
int httpsqs_settings_cacheleaf = 2048; /* 缓存叶子节点数。叶子节点缓存数为非叶子节点数的两倍。单位:条 */
int httpsqs_settings_mappedmemory = 104857600; /* 单位:字节 */
httpsqs_settings_pidfile = "/tmp/httpsqs.pid";
httpsqs_settings_auth = NULL; /* 验证密码 */
/* 命令行参数,暂时存储下面,便于进程重命名 */
int httpsqs_prename_num = 1;
char httpsqs_path_file[1024] = { 0 }; // httpsqs_path_file 为 httpsqs 程序的绝对路径
struct evbuffer *httpsqs_prename_buf; /* 原命令行参数 */
httpsqs_prename_buf = evbuffer_new();
readlink("/proc/self/exe", httpsqs_path_file, sizeof(httpsqs_path_file));
evbuffer_add_printf(httpsqs_prename_buf, "%s", httpsqs_path_file);
for (httpsqs_prename_num = 1; httpsqs_prename_num < argc; httpsqs_prename_num++) {
evbuffer_add_printf(httpsqs_prename_buf, " %s", argv[httpsqs_prename_num]);
}
/* process arguments */
while ((c = getopt(argc, argv, "l:p:x:t:s:c:m:i:a:dh")) != -1) {
switch (c) {
case 'l':
httpsqs_settings_listen = strdup(optarg);
break;
case 'p':
httpsqs_settings_port = atoi(optarg);
break;
case 'x':
httpsqs_settings_datapath = strdup(optarg); /* httpsqs数据库文件存放路径 */
if (access(httpsqs_settings_datapath, W_OK) != 0) { /* 如果目录不可写 */
if (access(httpsqs_settings_datapath, R_OK) == 0) { /* 如果目录可读 */
chmod(httpsqs_settings_datapath, S_IWOTH); /* 设置其他用户具可写入权限 */
} else { /* 如果不存在该目录,则创建 */
create_multilayer_dir(httpsqs_settings_datapath);
}
if (access(httpsqs_settings_datapath, W_OK) != 0) { /* 如果目录不可写 */
fprintf(stderr, "httpsqs database directory not writable
");
}
}
break;
case 't':
httpsqs_settings_timeout = atoi(optarg);
break;
case 's':
httpsqs_settings_syncinterval = atoi(optarg);
break;
case 'c':
httpsqs_settings_cachenonleaf = atoi(optarg);
httpsqs_settings_cacheleaf = httpsqs_settings_cachenonleaf * 2;
break;
case 'm':
httpsqs_settings_mappedmemory = atoi(optarg) * 1024 * 1024; /* 单位:M */
break;
case 'i':
httpsqs_settings_pidfile = strdup(optarg);
break;
case 'a':
httpsqs_settings_auth = strdup(optarg);
break;
case 'd':
httpsqs_settings_daemon = true;
break;
case 'h':
default:
show_help();
return 1;
}
}
/* 判断是否加了必填参数 -x */
if (httpsqs_settings_datapath == NULL) {
show_help();
fprintf(stderr, "Attention: Please use the indispensable argument: -x <path>
");
exit(1);
}
/* 数据表路径 */
int httpsqs_settings_dataname_len = 1024;
char *httpsqs_settings_dataname = (char *)tccalloc(1, httpsqs_settings_dataname_len);
sprintf(httpsqs_settings_dataname, "%s/httpsqs.db", httpsqs_settings_datapath);
/* 打开数据表 */
httpsqs_db_tcbdb = tcbdbnew();
tcbdbsetmutex(httpsqs_db_tcbdb); /* 开启线程互斥锁 */
tcbdbtune(httpsqs_db_tcbdb, 1024, 2048, 50000000, 8, 10, BDBTLARGE);
tcbdbsetcache(httpsqs_db_tcbdb, httpsqs_settings_cacheleaf, httpsqs_settings_cachenonleaf);
tcbdbsetxmsiz(httpsqs_db_tcbdb, httpsqs_settings_mappedmemory); /* 内存缓存大小 */
/* 判断表是否能打开 */
if(!tcbdbopen(httpsqs_db_tcbdb, httpsqs_settings_dataname, BDBOWRITER|BDBOCREAT)){
show_help();
fprintf(stderr, "Attention: Unable to open the database.
");
exit(1);
}
/* 释放变量所占内存 */
free(httpsqs_settings_dataname);
/* 如果加了-d参数,以守护进程运行 */
if (httpsqs_settings_daemon == true) {
pid_t pid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
}
/* 将进程号写入PID文件 */
FILE *fp_pidfile;
fp_pidfile = fopen(httpsqs_settings_pidfile, "w");
fprintf(fp_pidfile, "%d
", getpid());
fclose(fp_pidfile);
/* 重命名httpsqs主进程,便于ps -ef命令查看 */
prename_setproctitle_init(argc, argv, envp);
prename_setproctitle("[httpsqs: master process] %s", (char *)EVBUFFER_DATA(httpsqs_prename_buf));
/* 派生httpsqs子进程(工作进程) */
pid_t httpsqs_worker_pid_wait;
pid_t httpsqs_worker_pid = fork();
/* 如果派生进程失败,则退出程序 */
if (httpsqs_worker_pid < 0)
{
fprintf(stderr, "Error: %s:%d
", __FILE__, __LINE__);
exit(EXIT_FAILURE);
}
/* httpsqs父进程内容 */
if (httpsqs_worker_pid > 0)
{
/* 处理父进程接收到的kill信号 */
/* 忽略Broken Pipe信号 */
signal(SIGPIPE, SIG_IGN);
/* 处理kill信号 */
signal (SIGINT, kill_signal_master);
signal (SIGKILL, kill_signal_master);
signal (SIGQUIT, kill_signal_master);
signal (SIGTERM, kill_signal_master);
signal (SIGHUP, kill_signal_master);
/* 处理段错误信号 */
signal(SIGSEGV, kill_signal_master);
/* 如果子进程终止,则重新派生新的子进程 */
while (1)
{
httpsqs_worker_pid_wait = wait(NULL);
if (httpsqs_worker_pid_wait < 0)
{
continue;
}
usleep(100000);
httpsqs_worker_pid = fork();
if (httpsqs_worker_pid == 0)
{
break;
}
}
}
/* ---------------以下为httpsqs子进程内容------------------- */
/* 忽略Broken Pipe信号 */
signal(SIGPIPE, SIG_IGN);
/* 处理kill信号 */
signal (SIGINT, kill_signal_worker);
signal (SIGKILL, kill_signal_worker);
signal (SIGQUIT, kill_signal_worker);
signal (SIGTERM, kill_signal_worker);
signal (SIGHUP, kill_signal_worker);
/* 处理段错误信号 */
signal(SIGSEGV, kill_signal_worker);
/* 创建定时同步线程,定时将内存中的内容写入磁盘 */
pthread_t sync_worker_tid;
pthread_create(&sync_worker_tid, NULL, (void *) sync_worker, NULL);
/* 重命名httpsqs子进程,便于ps -ef命令查看 */
prename_setproctitle_init(argc, argv, envp);
prename_setproctitle("[httpsqs: worker process] %s", (char *)EVBUFFER_DATA(httpsqs_prename_buf));
evbuffer_free(httpsqs_prename_buf);
/* 请求处理部分 */
struct evhttp *httpd;
event_init();
httpd = evhttp_start(httpsqs_settings_listen, httpsqs_settings_port);
if (httpd == NULL) {
fprintf(stderr, "Error: Unable to listen on %s:%d
", httpsqs_settings_listen, httpsqs_settings_port);
kill(0, SIGTERM);
exit(1);
}
evhttp_set_timeout(httpd, httpsqs_settings_timeout);
/* Set a callback for requests to "/specific". */
/* evhttp_set_cb(httpd, "/select", select_handler, NULL); */
/* Set a callback for all other requests. */
evhttp_set_gencb(httpd, httpsqs_handler, NULL);
event_dispatch();
/* Not reached in this code as it is now. */
evhttp_free(httpd);
return 0;
}