diff --git a/rabbitmq_consumer/CMakeLists.txt b/rabbitmq_consumer/CMakeLists.txt new file mode 100644 index 000000000..30d16d630 --- /dev/null +++ b/rabbitmq_consumer/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required (VERSION 2.6) + +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /usr/lib/mariadb /usr/lib64/mariadb) +set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/include /usr/local/include /usr/include/mysql /usr/local/include/mysql /usr/include/mariadb /usr/local/include/mariadb) + +include(InstallRequiredSystemLibraries) + +project (consumer) + +find_path(MYSQL_INCLUDE_DIRS mysql.h) +find_library(MYSQL_LIBRARIES NAMES mysqlclient) +find_library(RABBITMQ_C_LIBRARIES NAMES rabbitmq) + +include_directories(${MYSQL_INCLUDE_DIRS}) +include_directories(${RABBITMQ_C_INCLUDE_DIRS}) +include_directories(${CMAKE_SOURCE_DIR}/inih) + +add_subdirectory (inih) +link_directories(${CMAKE_SOURCE_DIR}/inih) + +if(RABBITMQ_C_LIBRARIES AND MYSQL_LIBRARIES AND MYSQL_INCLUDE_DIRS) + +add_executable (consumer consumer.c ${MYSQL_LIBRARIES} ${RABBITMQ_C_LIBRARIES}) +target_link_libraries(consumer mysqlclient) +target_link_libraries(consumer rabbitmq) +target_link_libraries(consumer inih) +install(TARGETS consumer DESTINATION bin) +install(FILES consumer.cnf DESTINATION share/consumer) + + +else(RABBITMQ_C_LIBRARIES AND MYSQL_LIBRARIES AND MYSQL_INCLUDE_DIRS) +message(FATAL_ERROR "Error: Can not find requred libraries: libmysqld, librabbitmq.") + +endif(RABBITMQ_C_LIBRARIES AND MYSQL_LIBRARIES AND MYSQL_INCLUDE_DIRS) + +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "RabbitMQ Consumer Client") +set(CPACK_PACKAGE_NAME "RabbitMQ Consumer") +set(CPACK_GENERATOR "RPM") +set(CPACK_PACKAGE_VERSION_MAJOR "1") +set(CPACK_PACKAGE_VERSION_MINOR "0") +set(CPACK_RPM_PACKAGE_NAME "rabbitmq-consumer") +set(CPACK_RPM_PACKAGE_VENDOR "SkySQL Ab") +set(CPACK_RPM_PACKAGE_AUTOREQPROV " no") +include(CPack) \ No newline at end of file diff --git a/rabbitmq_consumer/Makefile b/rabbitmq_consumer/Makefile new file mode 100644 index 000000000..456727ef8 --- /dev/null +++ b/rabbitmq_consumer/Makefile @@ -0,0 +1,15 @@ +include buildconfig.inc + +CC=gcc +CFLAGS=-c -Wall -g -Iinih $(INCLUDE_DIRS) +LDFLAGS= $(LIBRARY_DIRS) -lrabbitmq -lmysqlclient +SRCS= inih/ini.c consumer.c +OBJ=$(SRCS:.c=.o) +all:$(OBJ) + $(CC) $(LDFLAGS) $(OBJ) -o consumer `mysql_config --cflags --libs` +%.o:%.c + $(CC) $(CFLAGS) $< -o $@ + +clean: + -rm *.o + -rm *~ diff --git a/rabbitmq_consumer/README b/rabbitmq_consumer/README new file mode 100644 index 000000000..6dc2fb982 --- /dev/null +++ b/rabbitmq_consumer/README @@ -0,0 +1,39 @@ +This program requires the librabbitmq and libmysqlclient libraries. + +librabbitmq-c - https://github.com/alanxz/rabbitmq-c +MariaDB Client Library for C 2.0 Series - https://mariadb.com/kb/en/mariadb/client-libraries/client-library-for-c/ + +Building with CMake: +'cmake .' + +Variables to pass for CMake: + +Path to headers -DCMAKE_INCLUDE_PATH= +Path to libraries -DCMAKE_LIBRARY_PATH= +Install prefix -DCMAKE_INSTALL_PREFIX= + + +Separate multiple folders with colons, for example: +'path1:path2:path3' + +After running CMake run 'make' to build the binaries and 'make package' to build RPMs. + +To build without CMake, use the provided makefile and update the +include and library directories 'in buildvars.inc' + +The configuration for the consumer client are red from 'consumer.cnf'. + +Options for the configuration file: + +hostname Hostname of the RabbitMQ server +port Port of the RabbitMQ server +vhost Virtual host location of the RabbitMQ server +user Username for the RabbitMQ server +passwd Password for the RabbitMQ server +queue Queue to consume from +dbserver Hostname of the SQL server +dbport Port of the SQL server +dbname Name of the SQL database to use +dbuser Database username +dbpasswd Database passwork +logfile Message log filename diff --git a/rabbitmq_consumer/buildconfig.inc b/rabbitmq_consumer/buildconfig.inc new file mode 100644 index 000000000..ab0f2f887 --- /dev/null +++ b/rabbitmq_consumer/buildconfig.inc @@ -0,0 +1,8 @@ +#Use the '-I' prefix for include and '-L' for library directories +#You can use multiple library and include directories + +#Path to the rabbitmq-c and mysqlclient libraries +LIBRARY_DIRS :=-L/usr/lib64 + +#path to headers +INCLUDE_DIRS :=-I/usr/include -I/usr/include/mysql \ No newline at end of file diff --git a/rabbitmq_consumer/consumer.c b/rabbitmq_consumer/consumer.c new file mode 100644 index 000000000..8ccabf401 --- /dev/null +++ b/rabbitmq_consumer/consumer.c @@ -0,0 +1,524 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct delivery_t +{ + uint64_t dtag; + amqp_message_t* message; + struct delivery_t *next,*prev; +}DELIVERY; + +typedef struct consumer_t +{ + char *hostname,*vhost,*user,*passwd,*queue,*dbserver,*dbname,*dbuser,*dbpasswd; + DELIVERY* query_stack; + int port,dbport; +}CONSUMER; + +static int all_ok; +static FILE* out_fd; +static CONSUMER* c_inst; +static char* DB_DATABASE = "CREATE DATABASE IF NOT EXISTS %s;"; +static char* DB_TABLE = "CREATE TABLE IF NOT EXISTS pairs (tag VARCHAR(64) PRIMARY KEY NOT NULL, query VARCHAR(2048), reply VARCHAR(2048), date_in DATETIME NOT NULL, date_out DATETIME DEFAULT NULL, counter INT DEFAULT 1)"; +static char* DB_INSERT = "INSERT INTO pairs(tag, query, date_in) VALUES ('%s','%s',FROM_UNIXTIME(%s))"; +static char* DB_UPDATE = "UPDATE pairs SET reply='%s', date_out=FROM_UNIXTIME(%s) WHERE tag='%s'"; +static char* DB_INCREMENT = "UPDATE pairs SET counter = counter+1, date_out=FROM_UNIXTIME(%s) WHERE query='%s'"; + +void sighndl(int signum) +{ + if(signum == SIGINT){ + all_ok = 0; + alarm(1); + } +} + +int handler(void* user, const char* section, const char* name, + const char* value) +{ + if(strcmp(section,"consumer") == 0){ + + if(strcmp(name,"hostname") == 0){ + c_inst->hostname = strdup(value); + }else if(strcmp(name,"vhost") == 0){ + c_inst->vhost = strdup(value); + }else if(strcmp(name,"port") == 0){ + c_inst->port = atoi(value); + }else if(strcmp(name,"user") == 0){ + c_inst->user = strdup(value); + }else if(strcmp(name,"passwd") == 0){ + c_inst->passwd = strdup(value); + }else if(strcmp(name,"queue") == 0){ + c_inst->queue = strdup(value); + }else if(strcmp(name,"dbserver") == 0){ + c_inst->dbserver = strdup(value); + }else if(strcmp(name,"dbport") == 0){ + c_inst->dbport = atoi(value); + }else if(strcmp(name,"dbname") == 0){ + c_inst->dbname = strdup(value); + }else if(strcmp(name,"dbuser") == 0){ + c_inst->dbuser = strdup(value); + }else if(strcmp(name,"dbpasswd") == 0){ + c_inst->dbpasswd = strdup(value); + }else if(strcmp(name,"logfile") == 0){ + out_fd = fopen(value,"ab"); + } + + } + + return 1; +} + +int isPair(amqp_message_t* a, amqp_message_t* b) +{ + int keylen = a->properties.correlation_id.len >= + b->properties.correlation_id.len ? + a->properties.correlation_id.len : + b->properties.correlation_id.len; + + return strncmp(a->properties.correlation_id.bytes, + b->properties.correlation_id.bytes, + keylen) == 0 ? 1 : 0; +} + +int connectToServer(MYSQL* server) +{ + + + mysql_init(server); + + mysql_options(server,MYSQL_READ_DEFAULT_GROUP,"client"); + mysql_options(server,MYSQL_OPT_USE_REMOTE_CONNECTION,0); + my_bool tr = 1; + mysql_options(server,MYSQL_OPT_RECONNECT,&tr); + + + MYSQL* result = mysql_real_connect(server, + c_inst->dbserver, + c_inst->dbuser, + c_inst->dbpasswd, + NULL, + c_inst->dbport, + NULL, + 0); + + + if(result==NULL){ + fprintf(out_fd,"Error: Could not connect to MySQL server: %s\n",mysql_error(server)); + return 0; + } + + int bsz = 1024; + char *qstr = calloc(bsz,sizeof(char)); + + + if(!qstr){ + fprintf(stderr, "Fatal Error: Cannot allocate enough memory.\n"); + return 0; + } + + + /**Connection ok, check that the database and table exist*/ + + memset(qstr,0,bsz); + sprintf(qstr,DB_DATABASE,c_inst->dbname); + if(mysql_query(server,qstr)){ + fprintf(stderr,"Error: Could not send query MySQL server: %s\n",mysql_error(server)); + } + memset(qstr,0,bsz); + sprintf(qstr,"USE %s;",c_inst->dbname); + if(mysql_query(server,qstr)){ + fprintf(stderr,"Error: Could not send query MySQL server: %s\n",mysql_error(server)); + } + + memset(qstr,0,bsz); + sprintf(qstr,DB_TABLE); + if(mysql_query(server,qstr)){ + fprintf(stderr,"Error: Could not send query MySQL server: %s\n",mysql_error(server)); + } + + free(qstr); + return 1; +} + +int sendMessage(MYSQL* server, amqp_message_t* msg) +{ + int buffsz = (int)((msg->body.len + 1)*2+1) + + (int)((msg->properties.correlation_id.len + 1)*2+1) + + strlen(DB_INSERT), + rval = 0; + char *qstr = calloc(buffsz,sizeof(char)), + *rawmsg = calloc((msg->body.len + 1),sizeof(char)), + *clnmsg = calloc(((msg->body.len + 1)*2+1),sizeof(char)), + *rawdate = calloc((msg->body.len + 1),sizeof(char)), + *clndate = calloc(((msg->body.len + 1)*2+1),sizeof(char)), + *rawtag = calloc((msg->properties.correlation_id.len + 1),sizeof(char)), + *clntag = calloc(((msg->properties.correlation_id.len + 1)*2+1),sizeof(char)); + + + + sprintf(qstr,"%.*s",(int)msg->body.len,(char *)msg->body.bytes); + fprintf(out_fd,"Received: %s\n",qstr); + char *ptr = strtok(qstr,"|"); + sprintf(rawdate,"%s",ptr); + ptr = strtok(NULL,"\n\0"); + if(ptr == NULL){ + fprintf(out_fd,"Message content not valid.\n"); + rval = 1; + goto cleanup; + } + sprintf(rawmsg,"%s",ptr); + sprintf(rawtag,"%.*s",(int)msg->properties.correlation_id.len,(char *)msg->properties.correlation_id.bytes); + memset(qstr,0,buffsz); + + mysql_real_escape_string(server,clnmsg,rawmsg,strnlen(rawmsg,msg->body.len + 1)); + mysql_real_escape_string(server,clndate,rawdate,strnlen(rawdate,msg->body.len + 1)); + mysql_real_escape_string(server,clntag,rawtag,strnlen(rawtag,msg->properties.correlation_id.len + 1)); + + if(strncmp(msg->properties.message_id.bytes, + "query",msg->properties.message_id.len) == 0) + { + + sprintf(qstr,DB_INCREMENT,clndate,clnmsg); + rval = mysql_query(server,qstr); + + if(mysql_affected_rows(server) == 0){ + memset(qstr,0,buffsz); + sprintf(qstr,DB_INSERT,clntag,clnmsg,clndate); + rval = mysql_query(server,qstr); + } + + }else if(strncmp(msg->properties.message_id.bytes, + "reply",msg->properties.message_id.len) == 0){ + + sprintf(qstr,DB_UPDATE,clnmsg,clndate,clntag); + rval = mysql_query(server,qstr); + + }else{ + rval = 1; + goto cleanup; + } + + + if(rval){ + fprintf(stderr,"Could not send query to SQL server:%s\n",mysql_error(server)); + goto cleanup; + } + + cleanup: + free(qstr); + free(rawmsg); + free(clnmsg); + free(rawdate); + free(clndate); + free(rawtag); + free(clntag); + + return rval; +} + +int sendToServer(MYSQL* server, amqp_message_t* a, amqp_message_t* b){ + + amqp_message_t *msg, *reply; + int buffsz = 2048; + char *qstr = calloc(buffsz,sizeof(char)); + + if(!qstr){ + fprintf(out_fd, "Fatal Error: Cannot allocate enough memory.\n"); + free(qstr); + return 0; + } + + if( a->properties.message_id.len == strlen("query") && + strncmp(a->properties.message_id.bytes,"query", + a->properties.message_id.len) == 0){ + + msg = a; + reply = b; + + }else{ + + msg = b; + reply = a; + + } + + + printf("pair: %.*s\nquery: %.*s\nreply: %.*s\n", + (int)msg->properties.correlation_id.len, + (char *)msg->properties.correlation_id.bytes, + (int)msg->body.len, + (char *)msg->body.bytes, + (int)reply->body.len, + (char *)reply->body.bytes); + + if((int)msg->body.len + + (int)reply->body.len + + (int)msg->properties.correlation_id.len + 50 >= buffsz) + { + char *qtmp = calloc(buffsz*2,sizeof(char)); + free(qstr); + + if(qtmp){ + qstr = qtmp; + buffsz *= 2; + }else{ + fprintf(stderr, "Fatal Error: Cannot allocate enough memory.\n"); + return 0; + } + + } + + char *rawmsg = calloc((msg->body.len + 1),sizeof(char)), + *clnmsg = calloc(((msg->body.len + 1)*2+1),sizeof(char)), + *rawrpl = calloc((reply->body.len + 1),sizeof(char)), + *clnrpl = calloc(((reply->body.len + 1)*2+1),sizeof(char)), + *rawtag = calloc((msg->properties.correlation_id.len + 1),sizeof(char)), + *clntag = calloc(((msg->properties.correlation_id.len + 1)*2+1),sizeof(char)); + + sprintf(rawmsg,"%.*s",(int)msg->body.len,(char *)msg->body.bytes); + sprintf(rawrpl,"%.*s",(int)reply->body.len,(char *)reply->body.bytes); + sprintf(rawtag,"%.*s",(int)msg->properties.correlation_id.len,(char *)msg->properties.correlation_id.bytes); + + char *ptr; + while((ptr = strchr(rawmsg,'\n'))){ + *ptr = ' '; + } + while((ptr = strchr(rawrpl,'\n'))){ + *ptr = ' '; + } + while((ptr = strchr(rawtag,'\n'))){ + *ptr = ' '; + } + + mysql_real_escape_string(server,clnmsg,rawmsg,strnlen(rawmsg,msg->body.len + 1)); + mysql_real_escape_string(server,clnrpl,rawrpl,strnlen(rawrpl,reply->body.len + 1)); + mysql_real_escape_string(server,clntag,rawtag,strnlen(rawtag,msg->properties.correlation_id.len + 1)); + + + + sprintf(qstr,"INSERT INTO pairs VALUES ('%s','%s','%s');",clnmsg,clnrpl,clntag); + free(rawmsg); + free(clnmsg); + free(rawrpl); + free(clnrpl); + free(rawtag); + free(clntag); + + if(mysql_query(server,qstr)){ + fprintf(stderr,"Could not send query to SQL server:%s\n",mysql_error(server)); + free(qstr); + return 0; + } + + free(qstr); + return 1; +} +int main(int argc, char** argv) +{ + int channel = 1, status = AMQP_STATUS_OK, cnfnlen; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + amqp_rpc_reply_t ret; + amqp_message_t *reply = NULL; + amqp_frame_t frame; + struct timeval timeout; + MYSQL db_inst; + char ch, *cnfname = NULL, *cnfpath = NULL; + static const char* fname = "consumer.cnf"; + + if((c_inst = calloc(1,sizeof(CONSUMER))) == NULL){ + fprintf(stderr, "Fatal Error: Cannot allocate enough memory.\n"); + return 1; + } + + if(signal(SIGINT,sighndl) == SIG_IGN){ + signal(SIGINT,SIG_IGN); + } + + while((ch = getopt(argc,argv,"c:"))!= -1){ + switch(ch){ + case 'c': + cnfnlen = strlen(optarg); + cnfpath = strdup(optarg); + break; + default: + + break; + } + } + + cnfname = calloc(cnfnlen + strlen(fname) + 1,sizeof(char)); + + if(cnfpath){ + + /**Config file path as argument*/ + strcpy(cnfname,cnfpath); + if(cnfpath[cnfnlen-1] != '/'){ + strcat(cnfname,"/"); + } + + } + + strcat(cnfname,fname); + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + all_ok = 1; + out_fd = NULL; + + + + /**Parse the INI file*/ + if(ini_parse(cnfname,handler,NULL) < 0){ + + /**Try to parse a config in the same directory*/ + if(ini_parse(fname,handler,NULL) < 0){ + fprintf(stderr, "Fatal Error: Error parsing configuration file!\n"); + goto fatal_error; + + } + } + + if(out_fd == NULL){ + out_fd = stdout; + } + + fprintf(out_fd,"\n--------------------------------------------------------------\n"); + + /**Confirm that all parameters were in the configuration file*/ + if(!c_inst->hostname||!c_inst->vhost||!c_inst->user|| + !c_inst->passwd||!c_inst->dbpasswd||!c_inst->queue|| + !c_inst->dbserver||!c_inst->dbname||!c_inst->dbuser){ + fprintf(stderr, "Fatal Error: Inadequate configuration file!\n"); + goto fatal_error; + } + + connectToServer(&db_inst); + + if((conn = amqp_new_connection()) == NULL || + (socket = amqp_tcp_socket_new(conn)) == NULL){ + fprintf(stderr, "Fatal Error: Cannot create connection object or socket.\n"); + goto fatal_error; + } + + if(amqp_socket_open(socket, c_inst->hostname, c_inst->port)){ + fprintf(stderr, "RabbitMQ Error: Cannot open socket.\n"); + goto error; + } + + ret = amqp_login(conn, c_inst->vhost, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, c_inst->user, c_inst->passwd); + + if(ret.reply_type != AMQP_RESPONSE_NORMAL){ + fprintf(stderr, "RabbitMQ Error: Cannot login to server.\n"); + goto error; + } + + amqp_channel_open(conn, channel); + ret = amqp_get_rpc_reply(conn); + + if(ret.reply_type != AMQP_RESPONSE_NORMAL){ + fprintf(stderr, "RabbitMQ Error: Cannot open channel.\n"); + goto error; + } + + reply = malloc(sizeof(amqp_message_t)); + if(!reply){ + fprintf(stderr, "Error: Cannot allocate enough memory.\n"); + goto error; + } + amqp_basic_consume(conn,channel,amqp_cstring_bytes(c_inst->queue),amqp_empty_bytes,0,0,0,amqp_empty_table); + + while(all_ok){ + + status = amqp_simple_wait_frame_noblock(conn,&frame,&timeout); + + /**No frames to read from server, possibly out of messages*/ + if(status == AMQP_STATUS_TIMEOUT){ + sleep(timeout.tv_sec); + continue; + } + + if(frame.payload.method.id == AMQP_BASIC_DELIVER_METHOD){ + + amqp_basic_deliver_t* decoded = (amqp_basic_deliver_t*)frame.payload.method.decoded; + + amqp_read_message(conn,channel,reply,0); + + if(sendMessage(&db_inst,reply)){ + + fprintf(stderr,"RabbitMQ Error: Received malformed message.\n"); + amqp_basic_reject(conn,channel,decoded->delivery_tag,0); + amqp_destroy_message(reply); + + }else{ + + amqp_basic_ack(conn,channel,decoded->delivery_tag,0); + amqp_destroy_message(reply); + + } + + }else{ + fprintf(stderr,"RabbitMQ Error: Received method from server: %s\n",amqp_method_name(frame.payload.method.id)); + all_ok = 0; + goto error; + } + + } + + fprintf(out_fd,"Shutting down...\n"); + error: + + mysql_close(&db_inst); + mysql_library_end(); + if(c_inst && c_inst->query_stack){ + + while(c_inst->query_stack){ + DELIVERY* d = c_inst->query_stack->next; + amqp_destroy_message(c_inst->query_stack->message); + free(c_inst->query_stack); + c_inst->query_stack = d; + } + + } + + amqp_channel_close(conn, channel, AMQP_REPLY_SUCCESS); + amqp_connection_close(conn, AMQP_REPLY_SUCCESS); + amqp_destroy_connection(conn); + fatal_error: + + if(out_fd){ + fclose(out_fd); + } + + + if(c_inst){ + + free(c_inst->hostname); + free(c_inst->vhost); + free(c_inst->user); + free(c_inst->passwd); + free(c_inst->queue); + free(c_inst->dbserver); + free(c_inst->dbname); + free(c_inst->dbuser); + free(c_inst->dbpasswd); + free(c_inst); + + } + + + + return all_ok; +} diff --git a/rabbitmq_consumer/consumer.cnf b/rabbitmq_consumer/consumer.cnf new file mode 100644 index 000000000..82296edc6 --- /dev/null +++ b/rabbitmq_consumer/consumer.cnf @@ -0,0 +1,28 @@ +# +#The options for the consumer are: +#hostname RabbitMQ hostname +#port RabbitMQ port +#vhost RabbitMQ virtual host +#user RabbitMQ username +#passwd RabbitMQ password +#queue Name of the queue to use +#dbserver SQL server name +#dbport SQL server port +#dbname Name of the databse to use +#dbuser SQL server username +#dbpasswd SQL server password +#logfile Message log filename +# +[consumer] +hostname=127.0.0.1 +port=5673 +vhost=/ +user=guest +passwd=guest +queue=q1 +dbserver=127.0.0.1 +dbport=3000 +dbname=mqpairs +dbuser=maxuser +dbpasswd=maxpwd +#logfile=consumer.log \ No newline at end of file diff --git a/rabbitmq_consumer/inih/._LICENSE.txt b/rabbitmq_consumer/inih/._LICENSE.txt new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/._LICENSE.txt differ diff --git a/rabbitmq_consumer/inih/._README.txt b/rabbitmq_consumer/inih/._README.txt new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/._README.txt differ diff --git a/rabbitmq_consumer/inih/._cpp b/rabbitmq_consumer/inih/._cpp new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/._cpp differ diff --git a/rabbitmq_consumer/inih/._examples b/rabbitmq_consumer/inih/._examples new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/._examples differ diff --git a/rabbitmq_consumer/inih/._extra b/rabbitmq_consumer/inih/._extra new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/._extra differ diff --git a/rabbitmq_consumer/inih/._ini.c b/rabbitmq_consumer/inih/._ini.c new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/._ini.c differ diff --git a/rabbitmq_consumer/inih/._ini.h b/rabbitmq_consumer/inih/._ini.h new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/._ini.h differ diff --git a/rabbitmq_consumer/inih/._tests b/rabbitmq_consumer/inih/._tests new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/._tests differ diff --git a/rabbitmq_consumer/inih/.gitignore b/rabbitmq_consumer/inih/.gitignore new file mode 100644 index 000000000..2a5429025 --- /dev/null +++ b/rabbitmq_consumer/inih/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +make.depend diff --git a/rabbitmq_consumer/inih/CMakeLists.txt b/rabbitmq_consumer/inih/CMakeLists.txt new file mode 100644 index 000000000..492566fd2 --- /dev/null +++ b/rabbitmq_consumer/inih/CMakeLists.txt @@ -0,0 +1 @@ +add_library(inih ini.c) diff --git a/rabbitmq_consumer/inih/LICENSE.txt b/rabbitmq_consumer/inih/LICENSE.txt new file mode 100755 index 000000000..44a3093a3 --- /dev/null +++ b/rabbitmq_consumer/inih/LICENSE.txt @@ -0,0 +1,27 @@ + +The "inih" library is distributed under the New BSD license: + +Copyright (c) 2009, Brush Technology +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Brush Technology nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/rabbitmq_consumer/inih/README.txt b/rabbitmq_consumer/inih/README.txt new file mode 100755 index 000000000..4bff76126 --- /dev/null +++ b/rabbitmq_consumer/inih/README.txt @@ -0,0 +1,5 @@ + +inih is a simple .INI file parser written in C, released under the New BSD +license (see LICENSE.txt). Go to the project home page for more info: + +http://code.google.com/p/inih/ diff --git a/rabbitmq_consumer/inih/cpp/._INIReader.cpp b/rabbitmq_consumer/inih/cpp/._INIReader.cpp new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/cpp/._INIReader.cpp differ diff --git a/rabbitmq_consumer/inih/cpp/._INIReader.h b/rabbitmq_consumer/inih/cpp/._INIReader.h new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/cpp/._INIReader.h differ diff --git a/rabbitmq_consumer/inih/cpp/._INIReaderTest.cpp b/rabbitmq_consumer/inih/cpp/._INIReaderTest.cpp new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/cpp/._INIReaderTest.cpp differ diff --git a/rabbitmq_consumer/inih/cpp/INIReader.cpp b/rabbitmq_consumer/inih/cpp/INIReader.cpp new file mode 100755 index 000000000..43f695149 --- /dev/null +++ b/rabbitmq_consumer/inih/cpp/INIReader.cpp @@ -0,0 +1,67 @@ +// Read an INI file into easy-to-access name/value pairs. + +#include +#include +#include +#include "../ini.h" +#include "INIReader.h" + +using std::string; + +INIReader::INIReader(string filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); +} + +int INIReader::ParseError() +{ + return _error; +} + +string INIReader::Get(string section, string name, string default_value) +{ + string key = MakeKey(section, name); + return _values.count(key) ? _values[key] : default_value; +} + +long INIReader::GetInteger(string section, string name, long default_value) +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +bool INIReader::GetBoolean(string section, string name, bool default_value) +{ + string valstr = Get(section, name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +string INIReader::MakeKey(string section, string name) +{ + string key = section + "." + name; + // Convert to lower case to make section/name lookups case-insensitive + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + return key; +} + +int INIReader::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + INIReader* reader = (INIReader*)user; + string key = MakeKey(section, name); + if (reader->_values[key].size() > 0) + reader->_values[key] += "\n"; + reader->_values[key] += value; + return 1; +} diff --git a/rabbitmq_consumer/inih/cpp/INIReader.h b/rabbitmq_consumer/inih/cpp/INIReader.h new file mode 100755 index 000000000..7571a29d2 --- /dev/null +++ b/rabbitmq_consumer/inih/cpp/INIReader.h @@ -0,0 +1,48 @@ +// Read an INI file into easy-to-access name/value pairs. + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// http://code.google.com/p/inih/ + +#ifndef __INIREADER_H__ +#define __INIREADER_H__ + +#include +#include + +// Read an INI file into easy-to-access name/value pairs. (Note that I've gone +// for simplicity here rather than speed, but it should be pretty decent.) +class INIReader +{ +public: + // Construct INIReader and parse given filename. See ini.h for more info + // about the parsing. + INIReader(std::string filename); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError(); + + // Get a string value from INI file, returning default_value if not found. + std::string Get(std::string section, std::string name, + std::string default_value); + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(std::string section, std::string name, long default_value); + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(std::string section, std::string name, bool default_value); + +private: + int _error; + std::map _values; + static std::string MakeKey(std::string section, std::string name); + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // __INIREADER_H__ diff --git a/rabbitmq_consumer/inih/cpp/INIReaderTest.cpp b/rabbitmq_consumer/inih/cpp/INIReaderTest.cpp new file mode 100755 index 000000000..cb13b62c1 --- /dev/null +++ b/rabbitmq_consumer/inih/cpp/INIReaderTest.cpp @@ -0,0 +1,20 @@ +// Example that shows simple usage of the INIReader class + +#include +#include "INIReader.h" + +int main() +{ + INIReader reader("../examples/test.ini"); + + if (reader.ParseError() < 0) { + std::cout << "Can't load 'test.ini'\n"; + return 1; + } + std::cout << "Config loaded from 'test.ini': version=" + << reader.GetInteger("protocol", "version", -1) << ", name=" + << reader.Get("user", "name", "UNKNOWN") << ", email=" + << reader.Get("user", "email", "UNKNOWN") << ", active=" + << reader.GetBoolean("user", "active", true) << "\n"; + return 0; +} diff --git a/rabbitmq_consumer/inih/examples/._config.def b/rabbitmq_consumer/inih/examples/._config.def new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/examples/._config.def differ diff --git a/rabbitmq_consumer/inih/examples/._ini_dump.c b/rabbitmq_consumer/inih/examples/._ini_dump.c new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/examples/._ini_dump.c differ diff --git a/rabbitmq_consumer/inih/examples/._ini_example.c b/rabbitmq_consumer/inih/examples/._ini_example.c new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/examples/._ini_example.c differ diff --git a/rabbitmq_consumer/inih/examples/._ini_xmacros.c b/rabbitmq_consumer/inih/examples/._ini_xmacros.c new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/examples/._ini_xmacros.c differ diff --git a/rabbitmq_consumer/inih/examples/._test.ini b/rabbitmq_consumer/inih/examples/._test.ini new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/examples/._test.ini differ diff --git a/rabbitmq_consumer/inih/examples/config.def b/rabbitmq_consumer/inih/examples/config.def new file mode 100755 index 000000000..f5c7ed5fe --- /dev/null +++ b/rabbitmq_consumer/inih/examples/config.def @@ -0,0 +1,8 @@ +// CFG(section, name, default) + +CFG(protocol, version, "0") + +CFG(user, name, "Fatty Lumpkin") +CFG(user, email, "fatty@lumpkin.com") + +#undef CFG diff --git a/rabbitmq_consumer/inih/examples/ini_dump.c b/rabbitmq_consumer/inih/examples/ini_dump.c new file mode 100755 index 000000000..5c8c6d115 --- /dev/null +++ b/rabbitmq_consumer/inih/examples/ini_dump.c @@ -0,0 +1,40 @@ +/* ini.h example that simply dumps an INI file without comments */ + +#include +#include +#include "../ini.h" + +static int dumper(void* user, const char* section, const char* name, + const char* value) +{ + static char prev_section[50] = ""; + + if (strcmp(section, prev_section)) { + printf("%s[%s]\n", (prev_section[0] ? "\n" : ""), section); + strncpy(prev_section, section, sizeof(prev_section)); + prev_section[sizeof(prev_section) - 1] = '\0'; + } + printf("%s = %s\n", name, value); + return 1; +} + +int main(int argc, char* argv[]) +{ + int error; + + if (argc <= 1) { + printf("Usage: ini_dump filename.ini\n"); + return 1; + } + + error = ini_parse(argv[1], dumper, NULL); + if (error < 0) { + printf("Can't read '%s'!\n", argv[1]); + return 2; + } + else if (error) { + printf("Bad config file (first error on line %d)!\n", error); + return 3; + } + return 0; +} diff --git a/rabbitmq_consumer/inih/examples/ini_example.c b/rabbitmq_consumer/inih/examples/ini_example.c new file mode 100755 index 000000000..962cef57a --- /dev/null +++ b/rabbitmq_consumer/inih/examples/ini_example.c @@ -0,0 +1,44 @@ +/* Example: parse a simple configuration file */ + +#include +#include +#include +#include "../ini.h" + +typedef struct +{ + int version; + const char* name; + const char* email; +} configuration; + +static int handler(void* user, const char* section, const char* name, + const char* value) +{ + configuration* pconfig = (configuration*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + if (MATCH("protocol", "version")) { + pconfig->version = atoi(value); + } else if (MATCH("user", "name")) { + pconfig->name = strdup(value); + } else if (MATCH("user", "email")) { + pconfig->email = strdup(value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +int main(int argc, char* argv[]) +{ + configuration config; + + if (ini_parse("test.ini", handler, &config) < 0) { + printf("Can't load 'test.ini'\n"); + return 1; + } + printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n", + config.version, config.name, config.email); + return 0; +} diff --git a/rabbitmq_consumer/inih/examples/ini_xmacros.c b/rabbitmq_consumer/inih/examples/ini_xmacros.c new file mode 100755 index 000000000..7d867acd6 --- /dev/null +++ b/rabbitmq_consumer/inih/examples/ini_xmacros.c @@ -0,0 +1,46 @@ +/* Parse a configuration file into a struct using X-Macros */ + +#include +#include +#include "../ini.h" + +/* define the config struct type */ +typedef struct { + #define CFG(s, n, default) char *s##_##n; + #include "config.def" +} config; + +/* create one and fill in its default values */ +config Config = { + #define CFG(s, n, default) default, + #include "config.def" +}; + +/* process a line of the INI file, storing valid values into config struct */ +int handler(void *user, const char *section, const char *name, + const char *value) +{ + config *cfg = (config *)user; + + if (0) ; + #define CFG(s, n, default) else if (strcmp(section, #s)==0 && \ + strcmp(name, #n)==0) cfg->s##_##n = strdup(value); + #include "config.def" + + return 1; +} + +/* print all the variables in the config, one per line */ +void dump_config(config *cfg) +{ + #define CFG(s, n, default) printf("%s_%s = %s\n", #s, #n, cfg->s##_##n); + #include "config.def" +} + +int main(int argc, char* argv[]) +{ + if (ini_parse("test.ini", handler, &Config) < 0) + printf("Can't load 'test.ini', using defaults\n"); + dump_config(&Config); + return 0; +} diff --git a/rabbitmq_consumer/inih/examples/test.ini b/rabbitmq_consumer/inih/examples/test.ini new file mode 100755 index 000000000..e06e7f9ba --- /dev/null +++ b/rabbitmq_consumer/inih/examples/test.ini @@ -0,0 +1,9 @@ +; Test config file for ini_example.c and INIReaderTest.cpp + +[protocol] ; Protocol configuration +version=6 ; IPv6 + +[user] +name = Bob Smith ; Spaces around '=' are stripped +email = bob@smith.com ; And comments (like this) ignored +active = true ; Test a boolean diff --git a/rabbitmq_consumer/inih/extra/._Makefile.static b/rabbitmq_consumer/inih/extra/._Makefile.static new file mode 100755 index 000000000..17b8574a4 Binary files /dev/null and b/rabbitmq_consumer/inih/extra/._Makefile.static differ diff --git a/rabbitmq_consumer/inih/extra/Makefile.static b/rabbitmq_consumer/inih/extra/Makefile.static new file mode 100755 index 000000000..0d6519e38 --- /dev/null +++ b/rabbitmq_consumer/inih/extra/Makefile.static @@ -0,0 +1,19 @@ +# Simple makefile to build inih as a static library using g++ + +SRC = ../ini.c +OBJ = $(SRC:.c=.o) +OUT = libinih.a +INCLUDES = -I.. +CCFLAGS = -g -O2 +CC = g++ + +default: $(OUT) + +.c.o: + $(CC) $(INCLUDES) $(CCFLAGS) $(EXTRACCFLAGS) -c $< -o $@ + +$(OUT): $(OBJ) + ar rcs $(OUT) $(OBJ) $(EXTRAARFLAGS) + +clean: + rm -f $(OBJ) $(OUT) diff --git a/rabbitmq_consumer/inih/ini.c b/rabbitmq_consumer/inih/ini.c new file mode 100755 index 000000000..9f9110eaf --- /dev/null +++ b/rabbitmq_consumer/inih/ini.c @@ -0,0 +1,176 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +http://code.google.com/p/inih/ + +*/ + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char* find_char_or_comment(const char* s, char c) +{ + int was_whitespace = 0; + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace((unsigned char)(*s)); + s++; + } + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, + int (*handler)(void*, const char*, const char*, + const char*), + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through file line by line */ + while (fgets(line, INI_MAX_LINE, file) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python ConfigParser, allow '#' comments at start of line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as continuation + of previous name's value (as per Python ConfigParser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') { + end = find_char_or_comment(start, ':'); + } + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, + int (*handler)(void*, const char*, const char*, const char*), + void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} diff --git a/rabbitmq_consumer/inih/ini.h b/rabbitmq_consumer/inih/ini.h new file mode 100755 index 000000000..b3a494a24 --- /dev/null +++ b/rabbitmq_consumer/inih/ini.h @@ -0,0 +1,72 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +http://code.google.com/p/inih/ + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + ConfigParser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ diff --git a/rabbitmq_consumer/inih/tests/._bad_comment.ini b/rabbitmq_consumer/inih/tests/._bad_comment.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._bad_comment.ini differ diff --git a/rabbitmq_consumer/inih/tests/._bad_multi.ini b/rabbitmq_consumer/inih/tests/._bad_multi.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._bad_multi.ini differ diff --git a/rabbitmq_consumer/inih/tests/._bad_section.ini b/rabbitmq_consumer/inih/tests/._bad_section.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._bad_section.ini differ diff --git a/rabbitmq_consumer/inih/tests/._baseline_multi.txt b/rabbitmq_consumer/inih/tests/._baseline_multi.txt new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._baseline_multi.txt differ diff --git a/rabbitmq_consumer/inih/tests/._baseline_single.txt b/rabbitmq_consumer/inih/tests/._baseline_single.txt new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._baseline_single.txt differ diff --git a/rabbitmq_consumer/inih/tests/._bom.ini b/rabbitmq_consumer/inih/tests/._bom.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._bom.ini differ diff --git a/rabbitmq_consumer/inih/tests/._multi_line.ini b/rabbitmq_consumer/inih/tests/._multi_line.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._multi_line.ini differ diff --git a/rabbitmq_consumer/inih/tests/._normal.ini b/rabbitmq_consumer/inih/tests/._normal.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._normal.ini differ diff --git a/rabbitmq_consumer/inih/tests/._unittest.bat b/rabbitmq_consumer/inih/tests/._unittest.bat new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._unittest.bat differ diff --git a/rabbitmq_consumer/inih/tests/._unittest.c b/rabbitmq_consumer/inih/tests/._unittest.c new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._unittest.c differ diff --git a/rabbitmq_consumer/inih/tests/._user_error.ini b/rabbitmq_consumer/inih/tests/._user_error.ini new file mode 100755 index 000000000..7fa6eb8a6 Binary files /dev/null and b/rabbitmq_consumer/inih/tests/._user_error.ini differ diff --git a/rabbitmq_consumer/inih/tests/bad_comment.ini b/rabbitmq_consumer/inih/tests/bad_comment.ini new file mode 100755 index 000000000..7f4602eb9 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/bad_comment.ini @@ -0,0 +1 @@ +This is an error diff --git a/rabbitmq_consumer/inih/tests/bad_multi.ini b/rabbitmq_consumer/inih/tests/bad_multi.ini new file mode 100755 index 000000000..655017500 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/bad_multi.ini @@ -0,0 +1 @@ + indented diff --git a/rabbitmq_consumer/inih/tests/bad_section.ini b/rabbitmq_consumer/inih/tests/bad_section.ini new file mode 100755 index 000000000..90e31ac09 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/bad_section.ini @@ -0,0 +1,5 @@ +[section1] +name1=value1 +[section2 +[section3 ; comment ] +name2=value2 diff --git a/rabbitmq_consumer/inih/tests/baseline_multi.txt b/rabbitmq_consumer/inih/tests/baseline_multi.txt new file mode 100755 index 000000000..637f75258 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/baseline_multi.txt @@ -0,0 +1,47 @@ +no_file.ini: e=-1 user=0 +... [section1] +... one=This is a test; +... two=1234; +... [ section 2 ] +... happy=4; +... sad=; +... [comment_test] +... test1=1;2;3; +... test2=2;3;4;this won't be a comment, needs whitespace before ';'; +... test;3=345; +... test4=4#5#6; +... [colon_tests] +... Content-Type=text/html; +... foo=bar; +... adams=42; +normal.ini: e=0 user=101 +... [section1] +... name1=value1; +... name2=value2; +bad_section.ini: e=3 user=102 +bad_comment.ini: e=1 user=102 +... [section] +... a=b; +... user=parse_error; +... c=d; +user_error.ini: e=3 user=104 +... [section1] +... single1=abc; +... multi=this is a; +... multi=multi-line value; +... single2=xyz; +... [section2] +... multi=a; +... multi=b; +... multi=c; +... [section3] +... single=ghi; +... multi=the quick; +... multi=brown fox; +... name=bob smith; +multi_line.ini: e=0 user=105 +bad_multi.ini: e=1 user=105 +... [bom_section] +... bom_name=bom_value; +... key“=value“; +bom.ini: e=0 user=107 diff --git a/rabbitmq_consumer/inih/tests/baseline_single.txt b/rabbitmq_consumer/inih/tests/baseline_single.txt new file mode 100755 index 000000000..30d8a2600 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/baseline_single.txt @@ -0,0 +1,43 @@ +no_file.ini: e=-1 user=0 +... [section1] +... one=This is a test; +... two=1234; +... [ section 2 ] +... happy=4; +... sad=; +... [comment_test] +... test1=1;2;3; +... test2=2;3;4;this won't be a comment, needs whitespace before ';'; +... test;3=345; +... test4=4#5#6; +... [colon_tests] +... Content-Type=text/html; +... foo=bar; +... adams=42; +normal.ini: e=0 user=101 +... [section1] +... name1=value1; +... name2=value2; +bad_section.ini: e=3 user=102 +bad_comment.ini: e=1 user=102 +... [section] +... a=b; +... user=parse_error; +... c=d; +user_error.ini: e=3 user=104 +... [section1] +... single1=abc; +... multi=this is a; +... single2=xyz; +... [section2] +... multi=a; +... [section3] +... single=ghi; +... multi=the quick; +... name=bob smith; +multi_line.ini: e=4 user=105 +bad_multi.ini: e=1 user=105 +... [bom_section] +... bom_name=bom_value; +... key“=value“; +bom.ini: e=0 user=107 diff --git a/rabbitmq_consumer/inih/tests/bom.ini b/rabbitmq_consumer/inih/tests/bom.ini new file mode 100755 index 000000000..44c519f47 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/bom.ini @@ -0,0 +1,3 @@ +[bom_section] +bom_name=bom_value +key“ = value“ diff --git a/rabbitmq_consumer/inih/tests/multi_line.ini b/rabbitmq_consumer/inih/tests/multi_line.ini new file mode 100755 index 000000000..d6eb10445 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/multi_line.ini @@ -0,0 +1,15 @@ +[section1] +single1 = abc +multi = this is a + multi-line value +single2 = xyz +[section2] +multi = a + b + c +[section3] +single: ghi +multi: the quick + brown fox +name = bob smith ; comment line 1 + ; comment line 2 diff --git a/rabbitmq_consumer/inih/tests/normal.ini b/rabbitmq_consumer/inih/tests/normal.ini new file mode 100755 index 000000000..787ff8174 --- /dev/null +++ b/rabbitmq_consumer/inih/tests/normal.ini @@ -0,0 +1,25 @@ +; This is an INI file +[section1] ; section comment +one=This is a test ; name=value comment +two = 1234 +; x=y + +[ section 2 ] +happy = 4 +sad = + +[empty] +; do nothing + +[comment_test] +test1 = 1;2;3 ; only this will be a comment +test2 = 2;3;4;this won't be a comment, needs whitespace before ';' +test;3 = 345 ; key should be "test;3" +test4 = 4#5#6 ; '#' only starts a comment at start of line +#test5 = 567 ; entire line commented + # test6 = 678 ; entire line commented, except in MULTILINE mode + +[colon_tests] +Content-Type: text/html +foo:bar +adams : 42 diff --git a/rabbitmq_consumer/inih/tests/unittest.bat b/rabbitmq_consumer/inih/tests/unittest.bat new file mode 100755 index 000000000..90969fe3f --- /dev/null +++ b/rabbitmq_consumer/inih/tests/unittest.bat @@ -0,0 +1,2 @@ +@call tcc ..\ini.c -I..\ -run unittest.c > baseline_multi.txt +@call tcc ..\ini.c -I..\ -DINI_ALLOW_MULTILINE=0 -run unittest.c > baseline_single.txt diff --git a/rabbitmq_consumer/inih/tests/unittest.c b/rabbitmq_consumer/inih/tests/unittest.c new file mode 100755 index 000000000..5e8f8904c --- /dev/null +++ b/rabbitmq_consumer/inih/tests/unittest.c @@ -0,0 +1,58 @@ +/* inih -- unit tests + +This works simply by dumping a bunch of info to standard output, which is +redirected to an output file (baseline_*.txt) and checked into the Subversion +repository. This baseline file is the test output, so the idea is to check it +once, and if it changes -- look at the diff and see which tests failed. + +Here's how I produced the two baseline files (with Tiny C Compiler): + +tcc -DINI_ALLOW_MULTILINE=1 ../ini.c -run unittest.c > baseline_multi.txt +tcc -DINI_ALLOW_MULTILINE=0 ../ini.c -run unittest.c > baseline_single.txt + +*/ + +#include +#include +#include +#include "../ini.h" + +int User; +char Prev_section[50]; + +int dumper(void* user, const char* section, const char* name, + const char* value) +{ + User = (int)user; + if (strcmp(section, Prev_section)) { + printf("... [%s]\n", section); + strncpy(Prev_section, section, sizeof(Prev_section)); + Prev_section[sizeof(Prev_section) - 1] = '\0'; + } + printf("... %s=%s;\n", name, value); + + return strcmp(name, "user")==0 && strcmp(value, "parse_error")==0 ? 0 : 1; +} + +void parse(const char* fname) { + static int u = 100; + int e; + + *Prev_section = '\0'; + e = ini_parse(fname, dumper, (void*)u); + printf("%s: e=%d user=%d\n", fname, e, User); + u++; +} + +int main(void) +{ + parse("no_file.ini"); + parse("normal.ini"); + parse("bad_section.ini"); + parse("bad_comment.ini"); + parse("user_error.ini"); + parse("multi_line.ini"); + parse("bad_multi.ini"); + parse("bom.ini"); + return 0; +} diff --git a/rabbitmq_consumer/inih/tests/user_error.ini b/rabbitmq_consumer/inih/tests/user_error.ini new file mode 100755 index 000000000..9798af35e --- /dev/null +++ b/rabbitmq_consumer/inih/tests/user_error.ini @@ -0,0 +1,4 @@ +[section] +a = b +user = parse_error +c = d diff --git a/rabbitmq_consumer/rabbitmq-message-consumer.spec b/rabbitmq_consumer/rabbitmq-message-consumer.spec new file mode 100644 index 000000000..bc43a7715 --- /dev/null +++ b/rabbitmq_consumer/rabbitmq-message-consumer.spec @@ -0,0 +1,55 @@ +%define _topdir %(echo $PWD)/ +%define name rabbitmq-message-consumer +%define release beta +%define version 1.0 +%define install_path /usr/local/skysql/maxscale/extra/consumer/ + +BuildRoot: %{buildroot} +Summary: rabbitmq-message-consumer +License: GPL +Name: %{name} +Version: %{version} +Release: %{release} +Source: %{name}-%{version}-%{release}.tar.gz +Prefix: / +Group: Development/Tools +Requires: maxscale + +%if 0%{?suse_version} +BuildRequires: gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc_s1 perl make libtool libopenssl-devel libaio libaio-devel mariadb libedit-devel librabbitmq-devel MariaDB-shared +%else +BuildRequires: gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc perl make libtool openssl-devel libaio libaio-devel librabbitmq-devel MariaDB-shared +%if 0%{?rhel} == 6 +BuildRequires: libedit-devel +%endif +%if 0%{?rhel} == 7 +BuildRequires: mariadb-devel mariadb-embedded-devel libedit-devel +%else +BuildRequires: MariaDB-devel MariaDB-server +%endif +%endif + +%description +rabbitmq-message-consumer + +%prep + +%setup -q + +%build +make clean +make + +%install +mkdir -p $RPM_BUILD_ROOT%{install_path} +cp consumer $RPM_BUILD_ROOT%{install_path} +cp consumer.cnf $RPM_BUILD_ROOT%{install_path} + +%clean + +%files +%defattr(-,root,root) +%{install_path}/consumer +%{install_path}/consumer.cnf + +%changelog