diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f524e1..3966239 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(maatframe) include_directories(${PROJECT_SOURCE_DIR}/include) set(CMAKE_C_FLAGS "-std=gnu99 -Wall") -set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wall") +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -Wall) include_directories(include) @@ -13,4 +13,5 @@ enable_testing() #add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) add_subdirectory(vendor) add_subdirectory(src) -add_subdirectory(test) \ No newline at end of file +add_subdirectory(test) +add_subdirectory(scanner) \ No newline at end of file diff --git a/src/cJSON.c b/deps/cJSON/cJSON.c similarity index 100% rename from src/cJSON.c rename to deps/cJSON/cJSON.c diff --git a/src/inc_internal/cJSON.h b/deps/cJSON/cJSON.h similarity index 100% rename from src/inc_internal/cJSON.h rename to deps/cJSON/cJSON.h diff --git a/deps/log/log.c b/deps/log/log.c new file mode 100644 index 0000000..ac82b33 --- /dev/null +++ b/deps/log/log.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2020 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "log.h" + +#define MAX_CALLBACKS 32 + +#define ALLOC(type, number) ((type *)calloc(sizeof(type), number)) + +typedef struct { + log_func_t fn; + void *user_data; + int level; +} Callback; + +struct log_handle +{ + int level; + int enable; + void *user_data; + log_lock_func_t lock; + char run_log_path[1024]; + char cur_log_file[1024]; + pthread_mutex_t mutex; + Callback callbacks[MAX_CALLBACKS]; +}; + +static unsigned char weekday_str[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + +static unsigned char month_str[12][4] = {"Jan", "Feb", "Mar", "Apr","May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +static int log_create_dir(const char *dir_path, int path_len) +{ + if(dir_path == NULL) + return -1; + + char *buf = (char *)calloc(path_len+1, 1); + int ret = -1; + + memcpy(buf, dir_path, path_len); + if(access(buf, R_OK) != 0) + { + if(mkdir(buf, 0755)!= 0) + ret = -1; + else + ret = 0; + } + else + ret = 1; + free(buf); + buf = NULL; + return ret; +} + +static void log_close_file(struct log_event *event) +{ + pthread_mutex_lock(&event->mutex); + FILE *fp=(FILE *)event->user_data; + if(fp != NULL) + { + fclose(fp); + event->user_data = NULL; + } + pthread_mutex_unlock(&event->mutex); + return; +} + +int log_open_file(char *file_name, struct log_event *event) +{ + FILE *fp = NULL; + log_close_file(event); + if(NULL == (fp = fopen(file_name, "a"))) + { + return -1; + } + memcpy(event->cur_log_file, file_name, strlen(file_name)); + event->user_data = fp; + return 0; +} + +static int log_create_path(const char *file_path) +{ + FILE *fp = NULL; + + if(file_path == NULL) + return 0; + + char *p_path = rindex(file_path, '/'); + if(p_path==0) + { + return 0; + } + + const char *p_cur = file_path; + int path_len = p_path - file_path; + int i = 0; + + if(log_create_dir(file_path, path_len) >= 0) + return 0; + + for(;i<=path_len;i++,p_cur++) + { + if(*p_cur == '/') + { + if(log_create_dir(file_path, i+1) < 0) + return -1; + } + } + if(NULL == (fp = fopen(file_path, "w"))) + { + return 0; + } + + return 1; +} + +int log_create_log_file(struct log_event *event) +{ + time_t t; + struct tm local_time; + char tmp_log_file_name[512]; + + FILE *fp = (FILE *)event->user_data; + + time(&t); + if(NULL == (localtime_r(&t, &local_time))) + { + return 0; + } + sprintf(tmp_log_file_name, "%s.%04d-%02d-%02d", event->run_log_path, local_time.tm_year + 1900, local_time.tm_mon + 1, local_time.tm_mday); + + if(fp == NULL) + { + if(0 != log_open_file(tmp_log_file_name, event)) return 0; + } + else + { + if (0 != memcmp(tmp_log_file_name, event->cur_log_file, strlen(tmp_log_file_name))) + { + if(0 != log_open_file(tmp_log_file_name, event))return 0; + } + } + + return 1; +} + +static void log_print_file(struct log_event *event) +{ + char buf[64]; + time_t t; + struct tm local_time; + const char *level_str_map[]= {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; + + time(&t); + if(NULL == (localtime_r(&t, &local_time))) return; + snprintf(buf, sizeof(buf), "%s %s %d %02d:%02d:%02d %d", weekday_str[local_time.tm_wday], + month_str[local_time.tm_mon], local_time.tm_mday, local_time.tm_hour, local_time.tm_min, local_time.tm_sec, local_time.tm_year+1900); + + log_create_log_file(event); + fprintf((FILE *)event->user_data, "%s, %s, %s, ", buf, level_str_map[event->level], event->module); + + vfprintf((FILE *)event->user_data, event->fmt, event->ap); + fprintf((FILE *)event->user_data, "\n"); + fflush((FILE *)event->user_data); +} + +static void log_print_console(struct log_event *event) +{ + char buf[64]={0}; + time_t t; + struct tm local_time; + const char *level_str_map[]= {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; + + time(&t); + if(NULL == (localtime_r(&t, &local_time))) return; + snprintf(buf, sizeof(buf), "%s %s %d %02d:%02d:%02d %d", weekday_str[local_time.tm_wday], + month_str[local_time.tm_mon], local_time.tm_mday, local_time.tm_hour, local_time.tm_min, local_time.tm_sec, local_time.tm_year+1900); + fprintf((FILE *)event->user_data, "%s, %s, %s, ", buf, level_str_map[event->level], event->module); + + vfprintf((FILE *)event->user_data, event->fmt, event->ap); + fprintf((FILE *)event->user_data, "\n"); + fflush((FILE *)event->user_data); +} + +void log_options_set_lock_fn(struct log_handle * handle, log_lock_func_t fn, void *user_data) +{ + struct log_handle *_handle_t = (struct log_handle *)handle; + if(_handle_t != NULL) + { + _handle_t->lock = fn; + _handle_t->user_data = user_data; + } +} + +void log_options_set_level(struct log_handle * handle, int level) +{ + struct log_handle *_handle_t = (struct log_handle *)handle; + if(_handle_t != NULL) + { + _handle_t->level = level; + } +} + +void log_options_set_enable(struct log_handle * handle, int enable) +{ + struct log_handle *_handle_t = (struct log_handle *)handle; + if(_handle_t != NULL) + { + _handle_t->enable = enable; + } +} + +int log_add_method_callback(void *handle, log_func_t fn, void *udata, int level) +{ + int i=0; + + struct log_handle *_handle_t = (struct log_handle *)handle; + if(_handle_t == NULL) + { + return -1; + } + + for (i = 0; i < MAX_CALLBACKS; i++) + { + if (!_handle_t->callbacks[i].fn) + { + _handle_t->callbacks[i] = (Callback) { fn, udata, level }; + return 0; + } + } + + return -1; +} + +int log_options_add_file_fp(struct log_handle *handle, int level) +{ + int ret=0; + FILE *fp=NULL; + + struct log_handle *_handle_t = (struct log_handle *)handle; + if(!_handle_t) + { + return -1; + } + ret =log_create_path(_handle_t->run_log_path); + if(ret==0) + { + fp=stderr; + } + return log_add_method_callback(handle, log_print_file, fp, level); +} + +int log_options_add_fp(struct log_handle * handle, FILE *fp, int level) +{ + return log_add_method_callback(handle, log_print_file, fp, level); +} + +static void log_lock(void *handle) +{ + struct log_handle *_handle_t = (struct log_handle *)handle; + if(_handle_t != NULL && _handle_t->lock) + { + _handle_t->lock(1, _handle_t->user_data); + } +} + +static void log_unlock(void *handle) +{ + struct log_handle *_handle_t = (struct log_handle *)handle; + if(_handle_t != NULL && _handle_t->lock) + { + _handle_t->lock(0, _handle_t->user_data); + } +} + +static void log_init_event(struct log_event *event, void *user_data) +{ + event->user_data = user_data; +} + +struct log_handle *log_handle_create(const char *file_path, int level) +{ + struct log_handle *_handle_t = ALLOC(struct log_handle, 1); + if(!_handle_t) + { + return NULL; + } + _handle_t->enable=1; + _handle_t->level = level; + strncpy(_handle_t->run_log_path, file_path, 1024); + pthread_mutex_init(&_handle_t->mutex,NULL); + + log_options_add_file_fp(_handle_t, level); + + return _handle_t; +} + +void log_handle_destroy(struct log_handle * handle) +{ + int i=0; + struct log_handle *_handle_t = (struct log_handle *)handle; + if(!_handle_t) + { + return; + } + if (_handle_t->user_data != NULL) + { + free(_handle_t->user_data); + _handle_t->user_data = NULL; + } + for(i=0; icallbacks && _handle_t->callbacks->user_data) + { + fclose(_handle_t->callbacks->user_data); + _handle_t->callbacks->user_data = NULL; + } + } + pthread_mutex_destroy(&(_handle_t->mutex)); + free(handle); + handle = NULL; + return; +} + +void log_print(struct log_handle *handle, int level, const char *module, const char *fmt, ...) +{ + struct log_event event; + memset(&event, 0, sizeof(event)); + + struct log_handle *_handle_t = (struct log_handle *)handle; + + event.fmt=fmt; + event.level=level; + event.module=module; + event.mutex=_handle_t->mutex; + event.cur_log_file=_handle_t->cur_log_file; + event.run_log_path=_handle_t->run_log_path; + + if(_handle_t->enable != 1 && level >= _handle_t->level) + { + log_init_event(&event, stderr); + va_start(event.ap, fmt); + log_print_console(&event); + va_end(event.ap); + } + + log_lock(handle); + + int i=0; + for(i=0; icallbacks[i].fn; i++) + { + Callback *cb = &_handle_t->callbacks[i]; + if (level >= cb->level) + { + log_init_event(&event, cb->user_data); + va_start(event.ap, fmt); + cb->fn(&event); + va_end(event.ap); + cb->user_data = event.user_data; + } + } + + log_unlock(handle); +} \ No newline at end of file diff --git a/deps/log/log.h b/deps/log/log.h new file mode 100644 index 0000000..c1cd5d3 --- /dev/null +++ b/deps/log/log.h @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2020 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include + +#define LOG_VERSION "0.1.0" + +struct log_handle; + +struct log_event +{ + int level; + va_list ap; + const char *fmt; + const char *module; + char *run_log_path; + char *cur_log_file; + void *user_data; + pthread_mutex_t mutex; +}; + +typedef void (*log_func_t)(struct log_event *ev); +typedef void (*log_lock_func_t)(int lock, void *udata); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_debug(handle, module, fmt, ...) log_print(handle, LOG_DEBUG, module, fmt, ##__VA_ARGS__) +#define log_trace(handle, module, fmt, ...) log_print(handle, LOG_TRACE, module, fmt, ##__VA_ARGS__) +#define log_info(handle, module, fmt, ...) log_print(handle, LOG_INFO, module, fmt, ##__VA_ARGS__) +#define log_warn(handle, module, fmt, ...) log_print(handle, LOG_WARN, module, fmt, ##__VA_ARGS__) +#define log_error(handle, module, fmt, ...) log_print(handle, LOG_ERROR, module, fmt, ##__VA_ARGS__) +#define log_fatal(handle, module, fmt, ...) log_print(handle, LOG_FATAL, module, fmt, ##__VA_ARGS__) + +void log_print(struct log_handle *, int level, const char *module, const char *fmt, ...); + +int log_options_add_file_fp(struct log_handle *, int level); +int log_options_add_fp(struct log_handle *, FILE *fp, int level); + +void log_options_set_lock_fn(struct log_handle *, log_lock_func_t fn, void *user_data); +void log_options_set_enable(struct log_handle *, int enable); +void log_options_set_level(struct log_handle *, int level); + +struct log_handle * log_handle_create(const char *file_path, int level); +void log_handle_destroy(struct log_handle *); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/deps/sds/Changelog b/deps/sds/Changelog deleted file mode 100644 index 26bd871..0000000 --- a/deps/sds/Changelog +++ /dev/null @@ -1,12 +0,0 @@ -Version 2.0 -=== - -* Better memory usage. A bit slower in certain workloads. -* sdscatfmt() replacement for sdscatprintf() for speed critical code added. -* Ability to easily switch allocator just changing sdsalloc.h -* No longer binary compatible with SDS v1.0. - -Version 1.0 -=== - -* Initial SDS stand alone verison. diff --git a/deps/sds/LICENSE b/deps/sds/LICENSE deleted file mode 100644 index d1199bc..0000000 --- a/deps/sds/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2006-2014, Salvatore Sanfilippo - -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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/deps/sds/Makefile b/deps/sds/Makefile deleted file mode 100644 index 045fa88..0000000 --- a/deps/sds/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all: sds-test - -sds-test: sds.c sds.h testhelp.h - $(CC) -o sds-test sds.c -Wall -std=c99 -pedantic -O2 -DSDS_TEST_MAIN - @echo ">>> Type ./sds-test to run the sds.c unit tests." - -clean: - rm -f sds-test diff --git a/deps/sds/README.md b/deps/sds/README.md deleted file mode 100644 index 94e0e44..0000000 --- a/deps/sds/README.md +++ /dev/null @@ -1,917 +0,0 @@ -Simple Dynamic Strings -=== - -**Notes about version 2**: this is an updated version of SDS in an attempt -to finally unify Redis, Disque, Hiredis, and the stand alone SDS versions. -This version is **NOT* binary compatible** with SDS verison 1, but the API -is 99% compatible so switching to the new lib should be trivial. - -Note that this version of SDS may be a slower with certain workloads, but -uses less memory compared to V1 since header size is dynamic and depends to -the string to alloc. - -Moreover it includes a few more API functions, notably `sdscatfmt` which -is a faster version of `sdscatprintf` that can be used for the simpler -cases in order to avoid the libc `printf` family functions performance -penalty. - -How SDS strings work -=== - -SDS is a string library for C designed to augment the limited libc string -handling functionalities by adding heap allocated strings that are: - -* Simpler to use. -* Binary safe. -* Computationally more efficient. -* But yet... Compatible with normal C string functions. - -This is achieved using an alternative design in which instead of using a C -structure to represent a string, we use a binary prefix that is stored -before the actual pointer to the string that is returned by SDS to the user. - - +--------+-------------------------------+-----------+ - | Header | Binary safe C alike string... | Null term | - +--------+-------------------------------+-----------+ - | - `-> Pointer returned to the user. - -Because of meta data stored before the actual returned pointer as a prefix, -and because of every SDS string implicitly adding a null term at the end of -the string regardless of the actual content of the string, SDS strings work -well together with C strings and the user is free to use them interchangeably -with other std C string functions that access the string in read-only. - -SDS was a C string I developed in the past for my everyday C programming needs, -later it was moved into Redis where it is used extensively and where it was -modified in order to be suitable for high performance operations. Now it was -extracted from Redis and forked as a stand alone project. - -Because of its many years life inside Redis, SDS provides both higher level -functions for easy strings manipulation in C, but also a set of low level -functions that make it possible to write high performance code without paying -a penalty for using an higher level string library. - -Advantages and disadvantages of SDS -=== - -Normally dynamic string libraries for C are implemented using a structure -that defines the string. The structure has a pointer field that is managed -by the string function, so it looks like this: - -```c -struct yourAverageStringLibrary { - char *buf; - size_t len; - ... possibly more fields here ... -}; -``` - -SDS strings as already mentioned don't follow this schema, and are instead -a single allocation with a prefix that lives *before* the address actually -returned for the string. - -There are advantages and disadvantages with this approach over the traditional -approach: - -**Disadvantage #1**: many functions return the new string as value, since sometimes SDS requires to create a new string with more space, so the most SDS API calls look like this: - -```c -s = sdscat(s,"Some more data"); -``` - -As you can see `s` is used as input for `sdscat` but is also set to the value -returned by the SDS API call, since we are not sure if the call modified the -SDS string we passed or allocated a new one. Not remembering to assign back -the return value of `sdscat` or similar functions to the variable holding -the SDS string will result in a bug. - -**Disadvantage #2**: if an SDS string is shared in different places in your program you have to modify all the references when you modify the string. However most of the times when you need to share SDS strings it is much better to encapsulate them into structures with a `reference count` otherwise it is too easy to incur into memory leaks. - -**Advantage #1**: you can pass SDS strings to functions designed for C functions without accessing a struct member or calling a function, like this: - -```c -printf("%s\n", sds_string); -``` - -In most other libraries this will be something like: - -```c -printf("%s\n", string->buf); -``` - -Or: - -```c -printf("%s\n", getStringPointer(string)); -``` - -**Advantage #2**: accessing individual chars is straightforward. C is a low level language so this is an important operation in many programs. With SDS strings accessing individual chars is very natural: - -```c -printf("%c %c\n", s[0], s[1]); -``` - -With other libraries your best chance is to assign `string->buf` (or call the function to get the string pointer) to a `char` pointer and work with this. However since the other libraries may reallocate the buffer implicitly every time you call a function that may modify the string you have to get a reference to the buffer again. - -**Advantage #3**: single allocation has better cache locality. Usually when you access a string created by a string library using a structure, you have two different allocations for the structure representing the string, and the actual buffer holding the string. Over the time the buffer is reallocated, and it is likely that it ends in a totally different part of memory compared to the structure itself. Since modern programs performances are often dominated by cache misses, SDS may perform better in many workloads. - -SDS basics -=== - -The type of SDS strings is just the char pointer `char *`. However SDS defines -an `sds` type as alias of `char *` in its header file: you should use the -`sds` type in order to make sure you remember that a given variable in your -program holds an SDS string and not a C string, however this is not mandatory. - -This is the simplest SDS program you can write that does something: - -```c -sds mystring = sdsnew("Hello World!"); -printf("%s\n", mystring); -sdsfree(mystring); - -output> Hello World! -``` - -The above small program already shows a few important things about SDS: - -* SDS strings are created, and heap allocated, via the `sdsnew()` function, or other similar functions that we'll see in a moment. -* SDS strings can be passed to `printf()` like any other C string. -* SDS strings require to be freed with `sdsfree()`, since they are heap allocated. - -Creating SDS strings ---- - -```c -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -``` - -There are many ways to create SDS strings: - -* The `sdsnew` function creates an SDS string starting from a C null terminated string. We already saw how it works in the above example. -* The `sdsnewlen` function is similar to `sdsnew` but instead of creating the string assuming that the input string is null terminated, it gets an additional length parameter. This way you can create a string using binary data: - - ```c - char buf[3]; - sds mystring; - - buf[0] = 'A'; - buf[1] = 'B'; - buf[2] = 'C'; - mystring = sdsnewlen(buf,3); - printf("%s of len %d\n", mystring, (int) sdslen(mystring)); - - output> ABC of len 3 - ``` - - Note: `sdslen` return value is casted to `int` because it returns a `size_t` -type. You can use the right `printf` specifier instead of casting. - -* The `sdsempty()` function creates an empty zero-length string: - - ```c - sds mystring = sdsempty(); - printf("%d\n", (int) sdslen(mystring)); - - output> 0 - ``` - -* The `sdsdup()` function duplicates an already existing SDS string: - - ```c - sds s1, s2; - - s1 = sdsnew("Hello"); - s2 = sdsdup(s1); - printf("%s %s\n", s1, s2); - - output> Hello Hello - ``` - -Obtaining the string length ---- - -```c -size_t sdslen(const sds s); -``` - -In the examples above we already used the `sdslen` function in order to get -the length of the string. This function works like `strlen` of the libc -except that: - -* It runs in constant time since the length is stored in the prefix of SDS strings, so calling `sdslen` is not expensive even when called with very large strings. -* The function is binary safe like any other SDS string function, so the length is the true length of the string regardless of the content, there is no problem if the string includes null term characters in the middle. - -As an example of the binary safeness of SDS strings, we can run the following -code: - -```c -sds s = sdsnewlen("A\0\0B",4); -printf("%d\n", (int) sdslen(s)); - -output> 4 -``` - -Note that SDS strings are always null terminated at the end, so even in that -case `s[4]` will be a null term, however printing the string with `printf` -would result in just `"A"` to be printed since libc will treat the SDS string -like a normal C string. - -Destroying strings ---- - -```c -void sdsfree(sds s); -``` - -The destroy an SDS string there is just to call `sdsfree` with the string -pointer. Note that even empty strings created with `sdsempty` need to be -destroyed as well otherwise they'll result into a memory leak. - -The function `sdsfree` does not perform any operation if instead of an SDS -string pointer, `NULL` is passed, so you don't need to check for `NULL` explicitly before calling it: - -```c -if (string) sdsfree(string); /* Not needed. */ -sdsfree(string); /* Same effect but simpler. */ -``` - -Concatenating strings ---- - -Concatenating strings to other strings is likely the operation you will end -using the most with a dynamic C string library. SDS provides different -functions to concatenate strings to existing strings. - -```c -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -``` - -The main string concatenation functions are `sdscatlen` and `sdscat` that are -identical, the only difference being that `sdscat` does not have an explicit -length argument since it expects a null terminated string. - -```c -sds s = sdsempty(); -s = sdscat(s, "Hello "); -s = sdscat(s, "World!"); -printf("%s\n", s); - -output> Hello World! -``` - -Sometimes you want to cat an SDS string to another SDS string, so you don't -need to specify the length, but at the same time the string does not need to -be null terminated but can contain any binary data. For this there is a -special function: - -```c -sds sdscatsds(sds s, const sds t); -``` - -Usage is straightforward: - -```c -sds s1 = sdsnew("aaa"); -sds s2 = sdsnew("bbb"); -s1 = sdscatsds(s1,s2); -sdsfree(s2); -printf("%s\n", s1); - -output> aaabbb -``` - -Sometimes you don't want to append any special data to the string, but you want -to make sure that there are at least a given number of bytes composing the -whole string. - -```c -sds sdsgrowzero(sds s, size_t len); -``` - -The `sdsgrowzero` function will do nothing if the current string length is -already `len` bytes, otherwise it will enlarge the string to `len` just padding -it with zero bytes. - -```c -sds s = sdsnew("Hello"); -s = sdsgrowzero(s,6); -s[5] = '!'; /* We are sure this is safe because of sdsgrowzero() */ -printf("%s\n', s); - -output> Hello! -``` - -Formatting strings ---- - -There is a special string concatenation function that accepts a `printf` alike -format specifier and cats the formatted string to the specified string. - -```c -sds sdscatprintf(sds s, const char *fmt, ...) { -``` - -Example: - -```c -sds s; -int a = 10, b = 20; -s = sdsnew("The sum is: "); -s = sdscatprintf(s,"%d+%d = %d",a,b,a+b); -``` - -Often you need to create SDS string directly from `printf` format specifiers. -Because `sdscatprintf` is actually a function that concatenates strings, all -you need is to concatenate your string to an empty string: - - -```c -char *name = "Anna"; -int loc = 2500; -sds s; -s = sdscatprintf(sdsempty(), "%s wrote %d lines of LISP\n", name, loc); -``` - -You can use `sdscatprintf` in order to convert numbers into SDS strings: - -```c -int some_integer = 100; -sds num = sdscatprintf(sdsempty(),"%d\n", some_integer); -``` - -However this is slow and we have a special function to make it efficient. - -Fast number to string operations ---- - -Creating an SDS string from an integer may be a common operation in certain -kind of programs, and while you may do this with `sdscatprintf` the performance -hit is big, so SDS provides a specialized function. - -```c -sds sdsfromlonglong(long long value); -``` - -Use it like this: - -```c -sds s = sdsfromlonglong(10000); -printf("%d\n", (int) sdslen(s)); - -output> 5 -``` - -Trimming strings and getting ranges ---- - -String trimming is a common operation where a set of characters are -removed from the left and the right of the string. Another useful operation -regarding strings is the ability to just take a range out of a larger -string. - -```c -void sdstrim(sds s, const char *cset); -void sdsrange(sds s, int start, int end); -``` - -SDS provides both the operations with the `sdstrim` and `sdsrange` functions. -However note that both functions work differently than most functions modifying -SDS strings since the return value is void: basically those functions always -destructively modify the passed SDS string, never allocating a new one, because -both trimming and ranges will never need more room: the operations can only -remove characters from the original string. - -Because of this behavior, both functions are fast and don't involve reallocation. - -This is an example of string trimming where newlines and spaces are removed -from an SDS strings: - -```c -sds s = sdsnew(" my string\n\n "); -sdstrim(s," \n"); -printf("-%s-\n",s); - -output> -my string- -``` - -Basically `sdstrim` takes the SDS string to trim as first argument, and a -null terminated set of characters to remove from left and right of the string. -The characters are removed as long as they are not interrupted by a character -that is not in the list of characters to trim: this is why the space between -`"my"` and `"string"` was preserved in the above example. - -Taking ranges is similar, but instead to take a set of characters, it takes -to indexes, representing the start and the end as specified by zero-based -indexes inside the string, to obtain the range that will be retained. - -```c -sds s = sdsnew("Hello World!"); -sdsrange(s,1,4); -printf("-%s-\n"); - -output> -ello- -``` - -Indexes can be negative to specify a position starting from the end of the -string, so that `-1` means the last character, `-2` the penultimate, and so forth: - -```c -sds s = sdsnew("Hello World!"); -sdsrange(s,6,-1); -printf("-%s-\n"); -sdsrange(s,0,-2); -printf("-%s-\n"); - -output> -World!- -output> -World- -``` - -`sdsrange` is very useful when implementing networking servers processing -a protocol or sending messages. For example the following code is used -implementing the write handler of the Redis Cluster message bus between -nodes: - -```c -void clusterWriteHandler(..., int fd, void *privdata, ...) { - clusterLink *link = (clusterLink*) privdata; - ssize_t nwritten = write(fd, link->sndbuf, sdslen(link->sndbuf)); - if (nwritten <= 0) { - /* Error handling... */ - } - sdsrange(link->sndbuf,nwritten,-1); - ... more code here ... -} -``` - -Every time the socket of the node we want to send the message to is writable -we attempt to write as much bytes as possible, and we use `sdsrange` in order -to remove from the buffer what was already sent. - -The function to queue new messages to send to some node in the cluster will -simply use `sdscatlen` in order to put more data in the send buffer. - -Note that the Redis Cluster bus implements a binary protocol, but since SDS -is binary safe this is not a problem, so the goal of SDS is not just to provide -an high level string API for the C programmer but also dynamically allocated -buffers that are easy to manage. - -String copying ---- - -The most dangerous and infamus function of the standard C library is probably -`strcpy`, so perhaps it is funny how in the context of better designed dynamic -string libraries the concept of copying strings is almost irrelevant. Usually -what you do is to create strings with the content you want, or concatenating -more content as needed. - -However SDS features a string copy function that is useful in performance -critical code sections, however I guess its practical usefulness is limited -as the function never managed to get called in the context of the 50k -lines of code composing the Redis code base. - -```c -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); -``` - -The string copy function of SDS is called `sdscpylen` and works like that: - -```c -s = sdsnew("Hello World!"); -s = sdscpylen(s,"Hello Superman!",15); -``` - -As you can see the function receives as input the SDS string `s`, but also -returns an SDS string. This is common to many SDS functions that modify the -string: this way the returned SDS string may be the original one modified -or a newly allocated one (for example if there was not enough room in the -old SDS string). - -The `sdscpylen` will simply replace what was in the old SDS string with the -new data you pass using the pointer and length argument. There is a similar -function called `sdscpy` that does not need a length but expects a null -terminated string instead. - -You may wonder why it makes sense to have a string copy function in the -SDS library, since you can simply create a new SDS string from scratch -with the new value instead of copying the value in an existing SDS string. -The reason is efficiency: `sdsnewlen` will always allocate a new string -while `sdscpylen` will try to reuse the existing string if there is enough -room to old the new content specified by the user, and will allocate a new -one only if needed. - -Quoting strings ---- - -In order to provide consistent output to the program user, or for debugging -purposes, it is often important to turn a string that may contain binary -data or special characters into a quoted string. Here for quoted string -we mean the common format for String literals in programming source code. -However today this format is also part of the well known serialization formats -like JSON and CSV, so it definitely escaped the simple goal of representing -literals strings in the source code of programs. - -An example of quoted string literal is the following: - -```c -"\x00Hello World\n" -``` - -The first byte is a zero byte while the last byte is a newline, so there are -two non alphanumerical characters inside the string. - -SDS uses a concatenation function for this goal, that concatenates to an -existing string the quoted string representation of the input string. - -```c -sds sdscatrepr(sds s, const char *p, size_t len); -``` - -The `scscatrepr` (where `repr` means *representation*) follows the usualy -SDS string function rules accepting a char pointer and a length, so you can -use it with SDS strings, normal C strings by using strlen() as `len` argument, -or binary data. The following is an example usage: - -```c -sds s1 = sdsnew("abcd"); -sds s2 = sdsempty(); -s[1] = 1; -s[2] = 2; -s[3] = '\n'; -s2 = sdscatrepr(s2,s1,sdslen(s1)); -printf("%s\n", s2); - -output> "a\x01\x02\n" -``` - -This is the rules `sdscatrepr` uses for conversion: - -* `\` and `"` are quoted with a backslash. -* It quotes special characters `'\n'`, `'\r'`, `'\t'`, `'\a'` and `'\b'`. -* All the other non printable characters not passing the `isprint` test are quoted in `\x..` form, that is: backslash followed by `x` followed by two digit hex number representing the character byte value. -* The function always adds initial and final double quotes characters. - -There is an SDS function that is able to perform the reverse conversion and is -documented in the *Tokenization* section below. - -Tokenization ---- - -Tokenization is the process of splitting a larger string into smaller strings. -In this specific case, the split is performed specifying another string that -acts as separator. For example in the following string there are two substrings -that are separated by the `|-|` separator: - -``` -foo|-|bar|-|zap -``` - -A more common separator that consists of a single character is the comma: - -``` -foo,bar,zap -``` - -In many progrems it is useful to process a line in order to obtain the sub -strings it is composed of, so SDS provides a function that returns an -array of SDS strings given a string and a separator. - -```c -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -``` - -As usually the function can work with both SDS strings or normal C strings. -The first two arguments `s` and `len` specify the string to tokenize, and the -other two arguments `sep` and `seplen` the separator to use during the -tokenization. The final argument `count` is a pointer to an integer that will -be set to the number of tokens (sub strings) returned. - -The return value is a heap allocated array of SDS strings. - -```c -sds *tokens; -int count, j; - -sds line = sdsnew("Hello World!"); -tokens = sdssplitlen(line,sdslen(line)," ",1,&count); - -for (j = 0; j < count; j++) - printf("%s\n", tokens[j]); -sdsfreesplitres(tokens,count); - -output> Hello -output> World! -``` - -The returned array is heap allocated, and the single elements of the array -are normal SDS strings. You can free everything calling `sdsfreesplitres` -as in the example. Alternativey you are free to release the array yourself -using the `free` function and use and/or free the individual SDS strings -as usually. - -A valid approach is to set the array elements you reused in some way to -`NULL`, and use `sdsfreesplitres` to free all the rest. - -Command line oriented tokenization ---- - -Splitting by a separator is a useful operation, but usually it is not enough -to perform one of the most common tasks involving some non trivial string -manipulation, that is, implementing a **Command Line Interface** for a program. - -This is why SDS also provides an additional function that allows you to split -arguments provided by the user via the keyboard in an interactive manner, or -via a file, network, or any other mean, into tokens. - -```c -sds *sdssplitargs(const char *line, int *argc); -``` - -The `sdssplitargs` function returns an array of SDS strings exactly like -`sdssplitlen`. The function to free the result is also identical, and is -`sdsfreesplitres`. The difference is in the way the tokenization is performed. - -For example if the input is the following line: - -``` -call "Sabrina" and "Mark Smith\n" -``` - -The function will return the following tokens: - -* "call" -* "Sabrina" -* "and" -* "Mark Smith\n" - -Basically different tokens need to be separated by one or more spaces, and -every single token can also be a quoted string in the same format that -`sdscatrepr` is able to emit. - -String joining ---- - -There are two functions doing the reverse of tokenization by joining strings -into a single one. - -```c -sds sdsjoin(char **argv, int argc, char *sep, size_t seplen); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); -``` - -The two functions take as input an array of strings of length `argc` and -a separator and its length, and produce as output an SDS string consisting -of all the specified strings separated by the specified separator. - -The difference between `sdsjoin` and `sdsjoinsds` is that the former accept -C null terminated strings as input while the latter requires all the strings -in the array to be SDS strings. However because of this only `sdsjoinsds` is -able to deal with binary data. - -```c -char *tokens[3] = {"foo","bar","zap"}; -sds s = sdsjoin(tokens,3,"|",1); -printf("%s\n", s); - -output> foo|bar|zap -``` - -Error handling ---- - -All the SDS functions that return an SDS pointer may also return `NULL` on -out of memory, this is basically the only check you need to perform. - -However many modern C programs handle out of memory simply aborting the program -so you may want to do this as well by wrapping `malloc` and other related -memory allocation calls directly. - -SDS internals and advanced usage -=== - -At the very beginning of this documentation it was explained how SDS strings -are allocated, however the prefix stored before the pointer returned to the -user was classified as an *header* without further details. For an advanced -usage it is better to dig more into the internals of SDS and show the -structure implementing it: - -```c -struct sdshdr { - int len; - int free; - char buf[]; -}; -``` - -As you can see, the structure may resemble the one of a conventional string -library, however the `buf` field of the structure is different since it is -not a pointer but an array without any length declared, so `buf` actually -points at the first byte just after the `free` integer. So in order to create -an SDS string we just allocate a piece of memory that is as large as the -`sdshdr` structure plus the length of our string, plus an additional byte -for the mandatory null term that every SDS string has. - -The `len` field of the structure is quite obvious, and is the current length -of the SDS string, always computed every time the string is modified via -SDS function calls. The `free` field instead represents the amount of free -memory in the current allocation that can be used to store more characters. - -So the actual SDS layout is this one: - - +------------+------------------------+-----------+---------------\ - | Len | Free | H E L L O W O R L D \n | Null term | Free space \ - +------------+------------------------+-----------+---------------\ - | - `-> Pointer returned to the user. - -You may wonder why there is some free space at the end of the string, it -looks like a waste. Actually after a new SDS string is created, there is no -free space at the end at all: the allocation will be as small as possible to -just hold the header, string, and null term. However other access patterns -will create extra free space at the end, like in the following program: - -```c -s = sdsempty(); -s = sdscat(s,"foo"); -s = sdscat(s,"bar"); -s = sdscat(s,"123"); -``` - -Since SDS tries to be efficient it can't afford to reallocate the string every -time new data is appended, since this would be very inefficient, so it uses -the **preallocation of some free space** every time you enlarge the string. - -The preallocation algorithm used is the following: every time the string -is reallocated in order to hold more bytes, the actual allocation size performed -is two times the minimum required. So for instance if the string currently -is holding 30 bytes, and we concatenate 2 more bytes, instead of allocating 32 -bytes in total SDS will allocate 64 bytes. - -However there is an hard limit to the allocation it can perform ahead, and is -defined by `SDS_MAX_PREALLOC`. SDS will never allocate more than 1MB of -additional space (by default, you can change this default). - -Shrinking strings ---- - -```c -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -``` - -Sometimes there are class of programs that require to use very little memory. -After strings concatenations, trimming, ranges, the string may end having -a non trivial amount of additional space at the end. - -It is possible to resize a string back to its minimal size in order to hold -the current content by using the function `sdsRemoveFreeSpace`. - -```c -s = sdsRemoveFreeSpace(s); -``` - -There is also a function that can be used in order to get the size of the -total allocation for a given string, and is called `sdsAllocSize`. - -```c -sds s = sdsnew("Ladies and gentlemen"); -s = sdscat(s,"... welcome to the C language."); -printf("%d\n", (int) sdsAllocSize(s)); -s = sdsRemoveFreeSpace(s); -printf("%d\n", (int) sdsAllocSize(s)); - -output> 109 -output> 59 -``` - -NOTE: SDS Low level API use cammelCase in order to warn you that you are playing with the fire. - -Manual modifications of SDS strings ---- - - void sdsupdatelen(sds s); - -Sometimes you may want to hack with an SDS string manually, without using -SDS functions. In the following example we implicitly change the length -of the string, however we want the logical length to reflect the null terminated -C string. - -The function `sdsupdatelen` does just that, updating the internal length -information for the specified string to the length obtained via `strlen`. - -```c -sds s = sdsnew("foobar"); -s[2] = '\0'; -printf("%d\n", sdslen(s)); -sdsupdatelen(s); -printf("%d\n", sdslen(s)); - -output> 6 -output> 2 -``` - -Sharing SDS strings ---- - -If you are writing a program in which it is advantageous to share the same -SDS string across different data structures, it is absolutely advised to -encapsulate SDS strings into structures that remember the number of references -of the string, with functions to increment and decrement the number of references. - -This approach is a memory management technique called *reference counting* and -in the context of SDS has two advantages: - -* It is less likely that you'll create memory leaks or bugs due to non freeing SDS strings or freeing already freed strings. -* You'll not need to update every reference to an SDS string when you modify it (since the new SDS string may point to a different memory location). - -While this is definitely a very common programming technique I'll outline -the basic ideas here. You create a structure like that: - -```c -struct mySharedString { - int refcount; - sds string; -} -``` - -When new strings are created, the structure is allocated and returned with -`refcount` set to 1. The you have two functions to change the reference count -of the shared string: - -* `incrementStringRefCount` will simply increment `refcount` of 1 in the structure. It will be called every time you add a reference to the string on some new data structure, variable, or whatever. -* `decrementStringRefCount` is used when you remove a reference. This function is however special since when the `refcount` drops to zero, it automatically frees the SDS string, and the `mySharedString` structure as well. - -Interactions with heap checkers ---- - -Because SDS returns pointers into the middle of memory chunks allocated with -`malloc`, heap checkers may have issues, however: - -* The popular Valgrind program will detect SDS strings are *possibly lost* memory and never as *definitely lost*, so it is easy to tell if there is a leak or not. I used Valgrind with Redis for years and every real leak was consistently detected as "definitely lost". -* OSX instrumentation tools don't detect SDS strings as leaks but are able to correctly handle pointers pointing to the middle of memory chunks. - -Zero copy append from syscalls ----- - -At this point you should have all the tools to dig more inside the SDS -library by reading the source code, however there is an interesting pattern -you can mount using the low level API exported, that is used inside Redis -in order to improve performances of the networking code. - -Using `sdsIncrLen()` and `sdsMakeRoomFor()` it is possible to mount the -following schema, to cat bytes coming from the kernel to the end of an -sds string without copying into an intermediate buffer: - -```c -oldlen = sdslen(s); -s = sdsMakeRoomFor(s, BUFFER_SIZE); -nread = read(fd, s+oldlen, BUFFER_SIZE); -... check for nread <= 0 and handle it ... -sdsIncrLen(s, nread); -``` - -`sdsIncrLen` is documented inside the source code of `sds.c`. - -Embedding SDS into your project -=== - -This is as simple as copying the following files inside your -project: - -* sds.c -* sds.h -* sdsalloc.h - -The source code is small and every C99 compiler should deal with -it without issues. - -Using a different allocator for SDS -=== - -Internally sds.c uses the allocator defined into `sdsalloc.h`. This header -file just defines macros for malloc, realloc and free, and by default libc -`malloc()`, `realloc()` and `free()` are used. Just edit this file in order -to change the name of the allocation functions. - -The program using SDS can call the SDS allocator in order to manipulate -SDS pointers (usually not needed but sometimes the program may want to -do advanced things) by using the API exported by SDS in order to call the -allocator used. This is especially useful when the program linked to SDS -is using a different allocator compared to what SDS is using. - -The API to access the allocator used by SDS is composed of three functions: `sds_malloc()`, `sds_realloc()` and `sds_free()`. - -Credits and license -=== - -SDS was created by Salvatore Sanfilippo and is released under the BDS two clause license. See the LICENSE file in this source distribution for more information. - -Oran Agra improved SDS version 2 by adding dynamic sized headers in order to -save memory for small strings and allow strings greater than 4GB. diff --git a/deps/sds/sds.c b/deps/sds/sds.c deleted file mode 100644 index b231684..0000000 --- a/deps/sds/sds.c +++ /dev/null @@ -1,1300 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#include -#include -#include -#include -#include -#include -#include "sds.h" -#include "sdsalloc.h" - -const char *SDS_NOINIT = "SDS_NOINIT"; - -static inline int sdsHdrSize(char type) { - switch(type&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return sizeof(struct sdshdr5); - case SDS_TYPE_8: - return sizeof(struct sdshdr8); - case SDS_TYPE_16: - return sizeof(struct sdshdr16); - case SDS_TYPE_32: - return sizeof(struct sdshdr32); - case SDS_TYPE_64: - return sizeof(struct sdshdr64); - } - return 0; -} - -static inline char sdsReqType(size_t string_size) { - if (string_size < 1<<5) - return SDS_TYPE_5; - if (string_size < 1<<8) - return SDS_TYPE_8; - if (string_size < 1<<16) - return SDS_TYPE_16; -#if (LONG_MAX == LLONG_MAX) - if (string_size < 1ll<<32) - return SDS_TYPE_32; - return SDS_TYPE_64; -#else - return SDS_TYPE_32; -#endif -} - -/* Create a new sds string with the content specified by the 'init' pointer - * and 'initlen'. - * If NULL is used for 'init' the string is initialized with zero bytes. - * If SDS_NOINIT is used, the buffer is left uninitialized; - * - * The string is always null-termined (all the sds strings are, always) so - * even if you create an sds string with: - * - * mystring = sdsnewlen("abc",3); - * - * You can print the string with printf() as there is an implicit \0 at the - * end of the string. However the string is binary safe and can contain - * \0 characters in the middle, as the length is stored in the sds header. */ -sds sdsnewlen(const void *init, size_t initlen) { - void *sh; - sds s; - char type = sdsReqType(initlen); - /* Empty strings are usually created in order to append. Use type 8 - * since type 5 is not good at this. */ - if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; - int hdrlen = sdsHdrSize(type); - unsigned char *fp; /* flags pointer. */ - - sh = s_malloc(hdrlen+initlen+1); - if (sh == NULL) return NULL; - if (init==SDS_NOINIT) - init = NULL; - else if (!init) - memset(sh, 0, hdrlen+initlen+1); - s = (char*)sh+hdrlen; - fp = ((unsigned char*)s)-1; - switch(type) { - case SDS_TYPE_5: { - *fp = type | (initlen << SDS_TYPE_BITS); - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - } - if (initlen && init) - memcpy(s, init, initlen); - s[initlen] = '\0'; - return s; -} - -/* Create an empty (zero length) sds string. Even in this case the string - * always has an implicit null term. */ -sds sdsempty(void) { - return sdsnewlen("",0); -} - -/* Create a new sds string starting from a null terminated C string. */ -sds sdsnew(const char *init) { - size_t initlen = (init == NULL) ? 0 : strlen(init); - return sdsnewlen(init, initlen); -} - -/* Duplicate an sds string. */ -sds sdsdup(const sds s) { - return sdsnewlen(s, sdslen(s)); -} - -/* Free an sds string. No operation is performed if 's' is NULL. */ -void sdsfree(sds s) { - if (s == NULL) return; - s_free((char*)s-sdsHdrSize(s[-1])); -} - -/* Set the sds string length to the length as obtained with strlen(), so - * considering as content only up to the first null term character. - * - * This function is useful when the sds string is hacked manually in some - * way, like in the following example: - * - * s = sdsnew("foobar"); - * s[2] = '\0'; - * sdsupdatelen(s); - * printf("%d\n", sdslen(s)); - * - * The output will be "2", but if we comment out the call to sdsupdatelen() - * the output will be "6" as the string was modified but the logical length - * remains 6 bytes. */ -void sdsupdatelen(sds s) { - size_t reallen = strlen(s); - sdssetlen(s, reallen); -} - -/* Modify an sds string in-place to make it empty (zero length). - * However all the existing buffer is not discarded but set as free space - * so that next append operations will not require allocations up to the - * number of bytes previously available. */ -void sdsclear(sds s) { - sdssetlen(s, 0); - s[0] = '\0'; -} - -/* Enlarge the free space at the end of the sds string so that the caller - * is sure that after calling this function can overwrite up to addlen - * bytes after the end of the string, plus one more byte for nul term. - * - * Note: this does not change the *length* of the sds string as returned - * by sdslen(), but only the free buffer space we have. */ -sds sdsMakeRoomFor(sds s, size_t addlen) { - void *sh, *newsh; - size_t avail = sdsavail(s); - size_t len, newlen; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - - /* Return ASAP if there is enough space left. */ - if (avail >= addlen) return s; - - len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - newlen = (len+addlen); - if (newlen < SDS_MAX_PREALLOC) - newlen *= 2; - else - newlen += SDS_MAX_PREALLOC; - - type = sdsReqType(newlen); - - /* Don't use type 5: the user is appending to the string and type 5 is - * not able to remember empty space, so sdsMakeRoomFor() must be called - * at every appending operation. */ - if (type == SDS_TYPE_5) type = SDS_TYPE_8; - - hdrlen = sdsHdrSize(type); - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+hdrlen; - } else { - /* Since the header size changes, need to move the string forward, - * and can't use realloc */ - newsh = s_malloc(hdrlen+newlen+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, newlen); - return s; -} - -/* Reallocate the sds string so that it has no free space at the end. The - * contained string remains not altered, but next concatenation operations - * will require a reallocation. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdsRemoveFreeSpace(sds s) { - void *sh, *newsh; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen, oldhdrlen = sdsHdrSize(oldtype); - size_t len = sdslen(s); - size_t avail = sdsavail(s); - sh = (char*)s-oldhdrlen; - - /* Return ASAP if there is no space left. */ - if (avail == 0) return s; - - /* Check what would be the minimum SDS header that is just good enough to - * fit this string. */ - type = sdsReqType(len); - hdrlen = sdsHdrSize(type); - - /* If the type is the same, or at least a large enough type is still - * required, we just realloc(), letting the allocator to do the copy - * only if really needed. Otherwise if the change is huge, we manually - * reallocate the string to use the different header type. */ - if (oldtype==type || type > SDS_TYPE_8) { - newsh = s_realloc(sh, oldhdrlen+len+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+oldhdrlen; - } else { - newsh = s_malloc(hdrlen+len+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, len); - return s; -} - -/* Return the total size of the allocation of the specified sds string, - * including: - * 1) The sds header before the pointer. - * 2) The string. - * 3) The free buffer at the end if any. - * 4) The implicit null term. - */ -size_t sdsAllocSize(sds s) { - size_t alloc = sdsalloc(s); - return sdsHdrSize(s[-1])+alloc+1; -} - -/* Return the pointer of the actual SDS allocation (normally SDS strings - * are referenced by the start of the string buffer). */ -void *sdsAllocPtr(sds s) { - return (void*) (s-sdsHdrSize(s[-1])); -} - -/* Increment the sds length and decrements the left free space at the - * end of the string according to 'incr'. Also set the null term - * in the new end of the string. - * - * This function is used in order to fix the string length after the - * user calls sdsMakeRoomFor(), writes something after the end of - * the current string, and finally needs to set the new length. - * - * Note: it is possible to use a negative increment in order to - * right-trim the string. - * - * Usage example: - * - * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the - * following schema, to cat bytes coming from the kernel to the end of an - * sds string without copying into an intermediate buffer: - * - * oldlen = sdslen(s); - * s = sdsMakeRoomFor(s, BUFFER_SIZE); - * nread = read(fd, s+oldlen, BUFFER_SIZE); - * ... check for nread <= 0 and handle it ... - * sdsIncrLen(s, nread); - */ -void sdsIncrLen(sds s, ssize_t incr) { - unsigned char flags = s[-1]; - size_t len; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char oldlen = SDS_TYPE_5_LEN(flags); - assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); - *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); - len = oldlen+incr; - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); - len = (sh->len += incr); - break; - } - default: len = 0; /* Just to avoid compilation warnings. */ - } - s[len] = '\0'; -} - -/* Grow the sds to have the specified length. Bytes that were not part of - * the original length of the sds will be set to zero. - * - * if the specified length is smaller than the current length, no operation - * is performed. */ -sds sdsgrowzero(sds s, size_t len) { - size_t curlen = sdslen(s); - - if (len <= curlen) return s; - s = sdsMakeRoomFor(s,len-curlen); - if (s == NULL) return NULL; - - /* Make sure added region doesn't contain garbage */ - memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ - sdssetlen(s, len); - return s; -} - -/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the - * end of the specified sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatlen(sds s, const void *t, size_t len) { - size_t curlen = sdslen(s); - - s = sdsMakeRoomFor(s,len); - if (s == NULL) return NULL; - memcpy(s+curlen, t, len); - sdssetlen(s, curlen+len); - s[curlen+len] = '\0'; - return s; -} - -/* Append the specified null termianted C string to the sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscat(sds s, const char *t) { - return sdscatlen(s, t, strlen(t)); -} - -/* Append the specified sds 't' to the existing sds 's'. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatsds(sds s, const sds t) { - return sdscatlen(s, t, sdslen(t)); -} - -/* Destructively modify the sds string 's' to hold the specified binary - * safe string pointed by 't' of length 'len' bytes. */ -sds sdscpylen(sds s, const char *t, size_t len) { - if (sdsalloc(s) < len) { - s = sdsMakeRoomFor(s,len-sdslen(s)); - if (s == NULL) return NULL; - } - memcpy(s, t, len); - s[len] = '\0'; - sdssetlen(s, len); - return s; -} - -/* Like sdscpylen() but 't' must be a null-termined string so that the length - * of the string is obtained with strlen(). */ -sds sdscpy(sds s, const char *t) { - return sdscpylen(s, t, strlen(t)); -} - -/* Helper for sdscatlonglong() doing the actual number -> string - * conversion. 's' must point to a string with room for at least - * SDS_LLSTR_SIZE bytes. - * - * The function returns the length of the null-terminated string - * representation stored at 's'. */ -#define SDS_LLSTR_SIZE 21 -int sdsll2str(char *s, long long value) { - char *p, aux; - unsigned long long v; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - v = (value < 0) ? -value : value; - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - if (value < 0) *p++ = '-'; - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Identical sdsll2str(), but for unsigned long long type. */ -int sdsull2str(char *s, unsigned long long v) { - char *p, aux; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Create an sds string from a long long value. It is much faster than: - * - * sdscatprintf(sdsempty(),"%lld\n", value); - */ -sds sdsfromlonglong(long long value) { - char buf[SDS_LLSTR_SIZE]; - int len = sdsll2str(buf,value); - - return sdsnewlen(buf,len); -} - -/* Like sdscatprintf() but gets va_list instead of being variadic. */ -sds sdscatvprintf(sds s, const char *fmt, va_list ap) { - va_list cpy; - char staticbuf[1024], *buf = staticbuf, *t; - size_t buflen = strlen(fmt)*2; - - /* We try to start using a static buffer for speed. - * If not possible we revert to heap allocation. */ - if (buflen > sizeof(staticbuf)) { - buf = (char *)s_malloc(buflen); - if (buf == NULL) return NULL; - } else { - buflen = sizeof(staticbuf); - } - - /* Try with buffers two times bigger every time we fail to - * fit the string in the current buffer size. */ - while(1) { - buf[buflen-2] = '\0'; - va_copy(cpy,ap); - vsnprintf(buf, buflen, fmt, cpy); - va_end(cpy); - if (buf[buflen-2] != '\0') { - if (buf != staticbuf) s_free(buf); - buflen *= 2; - buf = (char *)s_malloc(buflen); - if (buf == NULL) return NULL; - continue; - } - break; - } - - /* Finally concat the obtained string to the SDS string and return it. */ - t = sdscat(s, buf); - if (buf != staticbuf) s_free(buf); - return t; -} - -/* Append to the sds string 's' a string obtained using printf-alike format - * specifier. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("Sum is: "); - * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). - * - * Often you need to create a string from scratch with the printf-alike - * format. When this is the need, just use sdsempty() as the target string: - * - * s = sdscatprintf(sdsempty(), "... your format ...", args); - */ -sds sdscatprintf(sds s, const char *fmt, ...) { - va_list ap; - char *t; - va_start(ap, fmt); - t = sdscatvprintf(s,fmt,ap); - va_end(ap); - return t; -} - -/* This function is similar to sdscatprintf, but much faster as it does - * not rely on sprintf() family functions implemented by the libc that - * are often very slow. Moreover directly handling the sds string as - * new data is concatenated provides a performance improvement. - * - * However this function only handles an incompatible subset of printf-alike - * format specifiers: - * - * %s - C String - * %S - SDS string - * %i - signed int - * %I - 64 bit signed integer (long long, int64_t) - * %u - unsigned int - * %U - 64 bit unsigned integer (unsigned long long, uint64_t) - * %% - Verbatim "%" character. - */ -sds sdscatfmt(sds s, char const *fmt, ...) { - size_t initlen = sdslen(s); - const char *f = fmt; - long i; - va_list ap; - - /* To avoid continuous reallocations, let's start with a buffer that - * can hold at least two times the format string itself. It's not the - * best heuristic but seems to work in practice. */ - s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2); - va_start(ap,fmt); - f = fmt; /* Next format specifier byte to process. */ - i = initlen; /* Position of the next byte to write to dest str. */ - while(*f) { - char next, *str; - size_t l; - long long num; - unsigned long long unum; - - /* Make sure there is always space for at least 1 char. */ - if (sdsavail(s)==0) { - s = sdsMakeRoomFor(s,1); - } - - switch(*f) { - case '%': - next = *(f+1); - f++; - switch(next) { - case 's': - case 'S': - str = va_arg(ap,char*); - l = (next == 's') ? strlen(str) : sdslen(str); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,str,l); - sdsinclen(s,l); - i += l; - break; - case 'i': - case 'I': - if (next == 'i') - num = va_arg(ap,int); - else - num = va_arg(ap,long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsll2str(buf,num); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - case 'u': - case 'U': - if (next == 'u') - unum = va_arg(ap,unsigned int); - else - unum = va_arg(ap,unsigned long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsull2str(buf,unum); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - default: /* Handle %% and generally %. */ - s[i++] = next; - sdsinclen(s,1); - break; - } - break; - default: - s[i++] = *f; - sdsinclen(s,1); - break; - } - f++; - } - va_end(ap); - - /* Add null-term */ - s[i] = '\0'; - return s; -} - -/* Remove the part of the string from left and from right composed just of - * contiguous characters found in 'cset', that is a null terminted C string. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); - * s = sdstrim(s,"Aa. :"); - * printf("%s\n", s); - * - * Output will be just "HelloWorld". - */ -sds sdstrim(sds s, const char *cset) { - char *start, *end, *sp, *ep; - size_t len; - - sp = start = s; - ep = end = s+sdslen(s)-1; - while(sp <= end && strchr(cset, *sp)) sp++; - while(ep > sp && strchr(cset, *ep)) ep--; - len = (sp > ep) ? 0 : ((ep-sp)+1); - if (s != sp) memmove(s, sp, len); - s[len] = '\0'; - sdssetlen(s,len); - return s; -} - -/* Turn the string into a smaller (or equal) string containing only the - * substring specified by the 'start' and 'end' indexes. - * - * start and end can be negative, where -1 means the last character of the - * string, -2 the penultimate character, and so forth. - * - * The interval is inclusive, so the start and end characters will be part - * of the resulting string. - * - * The string is modified in-place. - * - * Example: - * - * s = sdsnew("Hello World"); - * sdsrange(s,1,-1); => "ello World" - */ -void sdsrange(sds s, ssize_t start, ssize_t end) { - size_t newlen, len = sdslen(s); - - if (len == 0) return; - if (start < 0) { - start = len+start; - if (start < 0) start = 0; - } - if (end < 0) { - end = len+end; - if (end < 0) end = 0; - } - newlen = (start > end) ? 0 : (end-start)+1; - if (newlen != 0) { - if (start >= (ssize_t)len) { - newlen = 0; - } else if (end >= (ssize_t)len) { - end = len-1; - newlen = (start > end) ? 0 : (end-start)+1; - } - } else { - start = 0; - } - if (start && newlen) memmove(s, s+start, newlen); - s[newlen] = 0; - sdssetlen(s,newlen); -} - -/* Apply tolower() to every character of the sds string 's'. */ -void sdstolower(sds s) { - size_t len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = tolower(s[j]); -} - -/* Apply toupper() to every character of the sds string 's'. */ -void sdstoupper(sds s) { - size_t len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = toupper(s[j]); -} - -/* Compare two sds strings s1 and s2 with memcmp(). - * - * Return value: - * - * positive if s1 > s2. - * negative if s1 < s2. - * 0 if s1 and s2 are exactly the same binary string. - * - * If two strings share exactly the same prefix, but one of the two has - * additional characters, the longer string is considered to be greater than - * the smaller one. */ -int sdscmp(const sds s1, const sds s2) { - size_t l1, l2, minlen; - int cmp; - - l1 = sdslen(s1); - l2 = sdslen(s2); - minlen = (l1 < l2) ? l1 : l2; - cmp = memcmp(s1,s2,minlen); - if (cmp == 0) return l1>l2? 1: (l1". - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatrepr(sds s, const char *p, size_t len) { - s = sdscatlen(s,"\"",1); - while(len--) { - switch(*p) { - case '\\': - case '"': - s = sdscatprintf(s,"\\%c",*p); - break; - case '\n': s = sdscatlen(s,"\\n",2); break; - case '\r': s = sdscatlen(s,"\\r",2); break; - case '\t': s = sdscatlen(s,"\\t",2); break; - case '\a': s = sdscatlen(s,"\\a",2); break; - case '\b': s = sdscatlen(s,"\\b",2); break; - default: - if (isprint(*p)) - s = sdscatprintf(s,"%c",*p); - else - s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); - break; - } - p++; - } - return sdscatlen(s,"\"",1); -} - -/* Helper function for sdssplitargs() that returns non zero if 'c' - * is a valid hex digit. */ -int is_hex_digit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -/* Helper function for sdssplitargs() that converts a hex digit into an - * integer from 0 to 15 */ -int hex_digit_to_int(char c) { - switch(c) { - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'a': case 'A': return 10; - case 'b': case 'B': return 11; - case 'c': case 'C': return 12; - case 'd': case 'D': return 13; - case 'e': case 'E': return 14; - case 'f': case 'F': return 15; - default: return 0; - } -} - -/* Split a line into arguments, where every argument can be in the - * following programming-language REPL-alike form: - * - * foo bar "newline are supported\n" and "\xff\x00otherstuff" - * - * The number of arguments is stored into *argc, and an array - * of sds is returned. - * - * The caller should free the resulting array of sds strings with - * sdsfreesplitres(). - * - * Note that sdscatrepr() is able to convert back a string into - * a quoted string in the same format sdssplitargs() is able to parse. - * - * The function returns the allocated tokens on success, even when the - * input string is empty, or NULL if the input contains unbalanced - * quotes or closed quotes followed by non space characters - * as in: "foo"bar or "foo' - */ -sds *sdssplitargs(const char *line, int *argc) { - const char *p = line; - char *current = NULL; - char **vector = NULL; - - *argc = 0; - while(1) { - /* skip blanks */ - while(*p && isspace(*p)) p++; - if (*p) { - /* get a token */ - int inq=0; /* set to 1 if we are in "quotes" */ - int insq=0; /* set to 1 if we are in 'single quotes' */ - int done=0; - - if (current == NULL) current = sdsempty(); - while(!done) { - if (inq) { - if (*p == '\\' && *(p+1) == 'x' && - is_hex_digit(*(p+2)) && - is_hex_digit(*(p+3))) - { - unsigned char byte; - - byte = (hex_digit_to_int(*(p+2))*16)+ - hex_digit_to_int(*(p+3)); - current = sdscatlen(current,(char*)&byte,1); - p += 3; - } else if (*p == '\\' && *(p+1)) { - char c; - - p++; - switch(*p) { - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'b': c = '\b'; break; - case 'a': c = '\a'; break; - default: c = *p; break; - } - current = sdscatlen(current,&c,1); - } else if (*p == '"') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else if (insq) { - if (*p == '\\' && *(p+1) == '\'') { - p++; - current = sdscatlen(current,"'",1); - } else if (*p == '\'') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else { - switch(*p) { - case ' ': - case '\n': - case '\r': - case '\t': - case '\0': - done=1; - break; - case '"': - inq=1; - break; - case '\'': - insq=1; - break; - default: - current = sdscatlen(current,p,1); - break; - } - } - if (*p) p++; - } - /* add the token to the vector */ - vector = (char **)s_realloc(vector,((*argc)+1)*sizeof(char*)); - vector[*argc] = current; - (*argc)++; - current = NULL; - } else { - /* Even on empty input string return something not NULL. */ - if (vector == NULL) vector = (char **)s_malloc(sizeof(void*)); - return vector; - } - } - -err: - while((*argc)--) - sdsfree(vector[*argc]); - s_free(vector); - if (current) sdsfree(current); - *argc = 0; - return NULL; -} - -/* Modify the string substituting all the occurrences of the set of - * characters specified in the 'from' string to the corresponding character - * in the 'to' array. - * - * For instance: sdsmapchars(mystring, "ho", "01", 2) - * will have the effect of turning the string "hello" into "0ell1". - * - * The function returns the sds string pointer, that is always the same - * as the input pointer since no resize is needed. */ -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { - size_t j, i, l = sdslen(s); - - for (j = 0; j < l; j++) { - for (i = 0; i < setlen; i++) { - if (s[j] == from[i]) { - s[j] = to[i]; - break; - } - } - } - return s; -} - -/* Join an array of C strings using the specified separator (also a C string). - * Returns the result as an sds string. */ -sds sdsjoin(char **argv, int argc, char *sep) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscat(join, argv[j]); - if (j != argc-1) join = sdscat(join,sep); - } - return join; -} - -/* Like sdsjoin, but joins an array of SDS strings. */ -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscatsds(join, argv[j]); - if (j != argc-1) join = sdscatlen(join,sep,seplen); - } - return join; -} - -/* Wrappers to the allocators used by SDS. Note that SDS will actually - * just use the macros defined into sdsalloc.h in order to avoid to pay - * the overhead of function calls. Here we define these wrappers only for - * the programs SDS is linked to, if they want to touch the SDS internals - * even if they use a different allocator. */ -void *sds_malloc(size_t size) { return s_malloc(size); } -void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } -void sds_free(void *ptr) { s_free(ptr); } - -#if defined(SDS_TEST_MAIN) -#include -#include "testhelp.h" -#include "limits.h" - -#define UNUSED(x) (void)(x) -int sdsTest(void) { - { - sds x = sdsnew("foo"), y; - - test_cond("Create a string and obtain the length", - sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) - - sdsfree(x); - x = sdsnewlen("foo",2); - test_cond("Create a string with specified length", - sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) - - x = sdscat(x,"bar"); - test_cond("Strings concatenation", - sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); - - x = sdscpy(x,"a"); - test_cond("sdscpy() against an originally longer string", - sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) - - x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); - test_cond("sdscpy() against an originally shorter string", - sdslen(x) == 33 && - memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) - - sdsfree(x); - x = sdscatprintf(sdsempty(),"%d",123); - test_cond("sdscatprintf() seems working in the base case", - sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); - test_cond("sdscatfmt() seems working in the base case", - sdslen(x) == 60 && - memcmp(x,"--Hello Hi! World -9223372036854775808," - "9223372036854775807--",60) == 0) - printf("[%s]\n",x); - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); - test_cond("sdscatfmt() seems working with unsigned numbers", - sdslen(x) == 35 && - memcmp(x,"--4294967295,18446744073709551615--",35) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," x"); - test_cond("sdstrim() works when all chars match", - sdslen(x) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," "); - test_cond("sdstrim() works when a single char remains", - sdslen(x) == 1 && x[0] == 'x') - - sdsfree(x); - x = sdsnew("xxciaoyyy"); - sdstrim(x,"xy"); - test_cond("sdstrim() correctly trims characters", - sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) - - y = sdsdup(x); - sdsrange(y,1,1); - test_cond("sdsrange(...,1,1)", - sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,-1); - test_cond("sdsrange(...,1,-1)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,-2,-1); - test_cond("sdsrange(...,-2,-1)", - sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,2,1); - test_cond("sdsrange(...,2,1)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,100); - test_cond("sdsrange(...,1,100)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,100,100); - test_cond("sdsrange(...,100,100)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("foo"); - y = sdsnew("foa"); - test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("bar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("aar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) - - sdsfree(y); - sdsfree(x); - x = sdsnewlen("\a\n\0foo\r",7); - y = sdscatrepr(sdsempty(),x,sdslen(x)); - test_cond("sdscatrepr(...data...)", - memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) - - { - unsigned int oldfree; - char *p; - int step = 10, j, i; - - sdsfree(x); - sdsfree(y); - x = sdsnew("0"); - test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); - - /* Run the test a few times in order to hit the first two - * SDS header types. */ - for (i = 0; i < 10; i++) { - int oldlen = sdslen(x); - x = sdsMakeRoomFor(x,step); - int type = x[-1]&SDS_TYPE_MASK; - - test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); - if (type != SDS_TYPE_5) { - test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); - oldfree = sdsavail(x); - } - p = x+oldlen; - for (j = 0; j < step; j++) { - p[j] = 'A'+j; - } - sdsIncrLen(x,step); - } - test_cond("sdsMakeRoomFor() content", - memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); - test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); - - sdsfree(x); - } - } - test_report() - return 0; -} -#endif - -#ifdef SDS_TEST_MAIN -int main(void) { - return sdsTest(); -} -#endif diff --git a/deps/sds/sds.h b/deps/sds/sds.h deleted file mode 100644 index 2d893f1..0000000 --- a/deps/sds/sds.h +++ /dev/null @@ -1,283 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __SDS_H -#define __SDS_H - -#ifdef __cpluscplus -extern "C" -{ -#endif - -#define SDS_MAX_PREALLOC (1024*1024) -extern const char *SDS_NOINIT; - -#include -#include -#include - -typedef char *sds; - -/* Note: sdshdr5 is never used, we just access the flags byte directly. - * However is here to document the layout of type 5 SDS strings. */ -struct __attribute__ ((__packed__)) sdshdr5 { - unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr8 { - uint8_t len; /* used */ - uint8_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr16 { - uint16_t len; /* used */ - uint16_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr32 { - uint32_t len; /* used */ - uint32_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr64 { - uint64_t len; /* used */ - uint64_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; - -#define SDS_TYPE_5 0 -#define SDS_TYPE_8 1 -#define SDS_TYPE_16 2 -#define SDS_TYPE_32 3 -#define SDS_TYPE_64 4 -#define SDS_TYPE_MASK 7 -#define SDS_TYPE_BITS 3 -#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); -#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) -#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) - -static inline size_t sdslen(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->len; - case SDS_TYPE_16: - return SDS_HDR(16,s)->len; - case SDS_TYPE_32: - return SDS_HDR(32,s)->len; - case SDS_TYPE_64: - return SDS_HDR(64,s)->len; - } - return 0; -} - -static inline size_t sdsavail(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - return 0; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - return sh->alloc - sh->len; - } - } - return 0; -} - -static inline void sdssetlen(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len = newlen; - break; - } -} - -static inline void sdsinclen(sds s, size_t inc) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len += inc; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len += inc; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len += inc; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len += inc; - break; - } -} - -/* sdsalloc() = sdsavail() + sdslen() */ -static inline size_t sdsalloc(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->alloc; - case SDS_TYPE_16: - return SDS_HDR(16,s)->alloc; - case SDS_TYPE_32: - return SDS_HDR(32,s)->alloc; - case SDS_TYPE_64: - return SDS_HDR(64,s)->alloc; - } - return 0; -} - -static inline void sdssetalloc(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - /* Nothing to do, this type has no total allocation info. */ - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = newlen; - break; - } -} - -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -void sdsfree(sds s); -sds sdsgrowzero(sds s, size_t len); -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -sds sdscatsds(sds s, const sds t); -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); - -sds sdscatvprintf(sds s, const char *fmt, va_list ap); -#ifdef __GNUC__ -sds sdscatprintf(sds s, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else -sds sdscatprintf(sds s, const char *fmt, ...); -#endif - -sds sdscatfmt(sds s, char const *fmt, ...); -sds sdstrim(sds s, const char *cset); -void sdsrange(sds s, ssize_t start, ssize_t end); -void sdsupdatelen(sds s); -void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); -sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -void sdstolower(sds s); -void sdstoupper(sds s); -sds sdsfromlonglong(long long value); -sds sdscatrepr(sds s, const char *p, size_t len); -sds *sdssplitargs(const char *line, int *argc); -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); -sds sdsjoin(char **argv, int argc, char *sep); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); - -/* Low level functions exposed to the user API */ -sds sdsMakeRoomFor(sds s, size_t addlen); -void sdsIncrLen(sds s, ssize_t incr); -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -void *sdsAllocPtr(sds s); - -/* Export the allocator used by SDS to the program using SDS. - * Sometimes the program SDS is linked to, may use a different set of - * allocators, but may want to allocate or free things that SDS will - * respectively free or allocate. */ -void *sds_malloc(size_t size); -void *sds_realloc(void *ptr, size_t size); -void sds_free(void *ptr); - -#ifdef REDIS_TEST -int sdsTest(int argc, char *argv[]); -#endif - -#ifdef __cpluscplus -} -#endif - -#endif diff --git a/deps/sds/sdsalloc.h b/deps/sds/sdsalloc.h deleted file mode 100644 index f43023c..0000000 --- a/deps/sds/sdsalloc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -/* SDS allocator selection. - * - * This file is used in order to change the SDS allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#define s_malloc malloc -#define s_realloc realloc -#define s_free free diff --git a/deps/sds/testhelp.h b/deps/sds/testhelp.h deleted file mode 100644 index 4503340..0000000 --- a/deps/sds/testhelp.h +++ /dev/null @@ -1,57 +0,0 @@ -/* This is a really minimal testing framework for C. - * - * Example: - * - * test_cond("Check if 1 == 1", 1==1) - * test_cond("Check if 5 > 10", 5 > 10) - * test_report() - * - * ---------------------------------------------------------------------------- - * - * Copyright (c) 2010-2012, Salvatore Sanfilippo - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __TESTHELP_H -#define __TESTHELP_H - -int __failed_tests = 0; -int __test_num = 0; -#define test_cond(descr,_c) do { \ - __test_num++; printf("%d - %s: ", __test_num, descr); \ - if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \ -} while(0); -#define test_report() do { \ - printf("%d tests, %d passed, %d failed\n", __test_num, \ - __test_num-__failed_tests, __failed_tests); \ - if (__failed_tests) { \ - printf("=== WARNING === We have failed tests here...\n"); \ - exit(1); \ - } \ -} while(0); - -#endif diff --git a/include/maat/maat.h b/include/maat/maat.h index 4a58c16..6ad2167 100644 --- a/include/maat/maat.h +++ b/include/maat/maat.h @@ -16,6 +16,11 @@ #ifndef _MAAT_H_ #define _MAAT_H_ +#ifdef __cpluscplus +extern "C" +{ +#endif + #include #include @@ -31,30 +36,66 @@ enum ip_type { IP_TYPE_V6 }; -struct ip_data { +typedef struct ip_data_s { enum ip_type type; union { uint32_t ipv4; uint32_t ipv6[4]; }; -}; +} ip_data_t; +#define MAAT_RULE_UPDATE_TYPE_FULL 1 +#define MAAT_RULE_UPDATE_TYPE_INC 2 + +typedef void maat_start_callback_t(int update_type, void *u_param); +typedef void maat_update_callback_t(int table_id, const char *table_line, void *u_para); +typedef void maat_finish_callback_t(void *u_para); + +typedef void maat_plugin_ex_new_func_t(int table_id, const char *key, const char *table_line, void **ad, long argl, void *argp); +typedef void maat_plugin_ex_free_func_t(int table_id, void **ad, long argl, void *argp); +typedef void maat_plugin_ex_dup_func_t(int table_id, void **to, void **from, long argl, void *argp); + +/* maat_instance options API */ struct maat_options; struct maat_options* maat_options_new(void); int maat_options_set_worker_thread_number(struct maat_options *opts, size_t nr_worker_threads); +int maat_options_set_rule_effect_interval_ms(struct maat_options *opts, int interval_ms); +int maat_options_set_rule_update_checking_interval_ms(struct maat_options *opts, int interval_ms); +int maat_options_set_gc_timeout_ms(struct maat_options *opts, int interval_ms); +int maat_options_set_instance_name(struct maat_options *opts, const char *instance_name, size_t name_len); +int maat_options_set_deferred_load_on(struct maat_options *opts); +int maat_options_set_iris_full_dir(struct maat_options *opts, const char *full_dir); +int maat_options_set_iris_inc_dir(struct maat_options *opts, const char *inc_dir); -struct maat *maat_new(struct maat_options options, const char* table_info_path); +/* maat_instance API */ +struct maat *maat_new(struct maat_options *opts, const char *table_info_path); void maat_free(struct maat *instance); +/* maat table API */ int maat_table_get_id(struct maat *instance, const char *table_name); +int maat_table_callback_register(struct maat *instance, int table_id, + maat_start_callback_t *start, + maat_update_callback_t *update, + maat_finish_callback_t *finish, + void *u_para); +/* maat plugin table API */ +int maat_plugin_table_ex_schema_register(struct maat *instance, int table_id, + maat_plugin_ex_new_func_t *new_func, + maat_plugin_ex_free_func_t *free_func, + maat_plugin_ex_dup_func_t *dup_func, + long argl, void *argp); + +void *maat_plugin_table_get_ex_data(struct maat *instance, int table_id, + const char *key, size_t key_len); +/* maat scan API */ struct maat_state; int maat_scan_integer(struct maat *instance, int table_id, int thread_id, unsigned int intval, int results[], size_t *n_result, struct maat_state *state); int maat_scan_ip(struct maat *instance, int table_id, int thread_id, - const struct ip_data *ip, int results[], size_t *n_result, + const ip_data_t *ip, int results[], size_t *n_result, struct maat_state *state); int maat_scan_string(struct maat *instance, int table_id, int thread_id, @@ -71,5 +112,8 @@ void maat_scan_stream_close(struct maat_stream **stream); void maat_state_reset(struct maat_state *state); +#ifdef __cpluscplus +} +#endif #endif \ No newline at end of file diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..295ce5d --- /dev/null +++ b/include/utils.h @@ -0,0 +1,27 @@ +/* +********************************************************************************************** +* File: maat_utils.h +* Description: maat utils entry +* Authors: Liu WenTan +* Date: 2022-10-31 +* Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved. +*********************************************************************************************** +*/ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#ifdef __cpluscplus +extern "C" +{ +#endif + +#include + +#define ALLOC(type, number) ((type *)calloc(sizeof(type), number)) + +#ifdef __cpluscplus +} +#endif + +#endif diff --git a/scanner/CMakeLists.txt b/scanner/CMakeLists.txt new file mode 100644 index 0000000..f472437 --- /dev/null +++ b/scanner/CMakeLists.txt @@ -0,0 +1,10 @@ +add_definitions(-D_GNU_SOURCE) +add_definitions(-fPIC) + +include_directories(${PROJECT_SOURCE_DIR}/deps) +include_directories(${PROJECT_SOURCE_DIR}/include) + +add_library(adapter-static adapter_hs.cpp bool_matcher.cpp) + +#add_executable(adapter_gtest adapter_hs_gtest.cpp) +target_link_libraries(adapter-static hyperscan_static hyperscan_runtime_static ipmatcher-static) diff --git a/scanner/IPMatcher.h b/scanner/IPMatcher.h new file mode 100644 index 0000000..0e387f3 --- /dev/null +++ b/scanner/IPMatcher.h @@ -0,0 +1,117 @@ +/* + * + * Copyright (c) 2020 + * String Algorithms Research Group + * Institute of Information Engineering, Chinese Academy of Sciences (IIE-CAS) + * National Engineering Laboratory for Information Security Technologies (NELIST) + * All rights reserved + * + * Written by: LU YUHAI (luyuhai@iie.ac.cn) + * Last modification: 2020-04-20 + * + * This code is the exclusive and proprietary property of IIE-CAS and NELIST. + * Usage for direct or indirect commercial advantage is not allowed without + * written permission from the authors. + * + */ + +#ifndef H_IP_MATCHER_H +#define H_IP_MATCHER_H +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + enum IP_TYPE + { + IPv4, + IPv6 + }; + + /* ĵIPv4 */ + struct ipv4_range + { + unsigned int start_ip; /* IPΧ½ */ + unsigned int end_ip; /* IPΧϽ */ + }; + + /* ĵIPv6 */ + struct ipv6_range + { + unsigned int start_ip[4]; /* IPΧ½ */ + unsigned int end_ip[4]; /* IPΧϽ */ + }; + + /* ͨõip */ + struct ip_rule + { + enum IP_TYPE type; /* ͣipv4ipv6 */ + unsigned int rule_id; /* ID */ + void* user_tag; /* ûԶݣʱƥ */ + union + { + struct ipv4_range ipv4_rule; /*ĵIPv4*/ + struct ipv6_range ipv6_rule; /*ĵIPv6*/ + }; + }; + + /* ͨõĴɨ */ + struct ip_data + { + enum IP_TYPE type; /* ͣipv4ipv6 */ + union /* rule_typeݸipv4ipv6 */ + { + unsigned int ipv4; /* ipv4*/ + unsigned int ipv6[4]; /* ipv6*/ + }; + }; + + + /* ʽɨ */ + struct scan_result + { + unsigned int rule_id; /* ID */ + void * tag; /* ûԶݣʱƥ */ + }; + + + struct ip_matcher; + + /* + ܣĹɨ + + rules[in]һip + rule_num[in]Ĺ + mem_use[out]ڴ + ֵ + ipɨ,ؿָɨʧ + */ + struct ip_matcher* ip_matcher_new(struct ip_rule * rules, size_t rule_num, size_t * mem_use); + + /* + ܣipɨipݽɨ + + matcher[in]ipɨ + data[in]Ĵɨip + result[in]ؽ洢 + size[in]ĴС + ֵ + н<=sizeֵΪ-1ʾ + + */ + int ip_matcher_match(struct ip_matcher* matcher, struct ip_data * data, struct scan_result* result, size_t size); + + /* + ܣһipɨ + + matcher[in]ٵipɨָ + */ + void ip_matcher_free(struct ip_matcher* matcher); + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(H_IP_MATCHER_H) */ diff --git a/src/adapter_hs.cpp b/scanner/adapter_hs.cpp similarity index 94% rename from src/adapter_hs.cpp rename to scanner/adapter_hs.cpp index 00f8738..46f0896 100644 --- a/src/adapter_hs.cpp +++ b/scanner/adapter_hs.cpp @@ -13,11 +13,10 @@ #include #include -#include "sds/sds.h" #include "adapter_hs.h" #include "uthash/utarray.h" #include "uthash/uthash.h" -#include "maat_utils.h" +#include "utils.h" #include "bool_matcher.h" struct adpt_hs_compile_data { @@ -69,7 +68,7 @@ int adpt_hs_alloc_scratch(struct adapter_hs_runtime *hs_rt, size_t nr_worker_thr } if (hs_alloc_scratch(database, &hs_rt->scratchs[0]) != HS_SUCCESS) { - // log_error("ERROR: Unable to allocate scratch space. Exiting.\n"); + fprintf(stderr, "ERROR: Unable to allocate scratch space. Exiting.\n"); hs_free_database(database); return -1; } @@ -77,14 +76,14 @@ int adpt_hs_alloc_scratch(struct adapter_hs_runtime *hs_rt, size_t nr_worker_thr for (size_t i = 1; i < nr_worker_threads; i++) { hs_error_t err = hs_clone_scratch(hs_rt->scratchs[0], &hs_rt->scratchs[i]); if (err != HS_SUCCESS) { - // log_error("Unable to clone scratch prototype"); + fprintf(stderr, "Unable to clone scratch prototype"); hs_free_database(database); return -1; } err = hs_scratch_size(hs_rt->scratchs[i], &hs_rt->scratch_size); if (err != HS_SUCCESS) { - // log_error("Unable to query scratch size"); + fprintf(stderr, "Unable to query scratch size"); hs_free_database(database); return -1; } @@ -115,9 +114,8 @@ int adpt_hs_build_database(struct adapter_hs_runtime *hs_rt, literal_cd->ids, literal_cd->pattern_lens, literal_cd->n_patterns, scan_mode, NULL, &hs_rt->literal_db, &compile_err); if (err != HS_SUCCESS) { - // log_error if (compile_err) { - printf("compile error: %s", compile_err->message); + fprintf(stderr, "%s compile error: %s", __func__, compile_err->message); } hs_free_compile_error(compile_err); @@ -130,9 +128,8 @@ int adpt_hs_build_database(struct adapter_hs_runtime *hs_rt, regex_cd->ids, NULL, regex_cd->n_patterns, scan_mode, NULL, &hs_rt->regex_db, &compile_err); if (err != HS_SUCCESS) { - // log_error if (compile_err) { - printf("compile error: %s", compile_err->message); + fprintf(stderr, "%s compile error: %s", __func__, compile_err->message); } hs_free_compile_error(compile_err); goto error; @@ -189,7 +186,7 @@ struct adapter_hs *adapter_hs_initialize(int scan_mode, size_t nr_worker_threads { if ((scan_mode != SCAN_MODE_BLOCK && scan_mode != SCAN_MODE_STREAM) || 0 == nr_worker_threads || NULL == expr_array || 0 == n_expr_array) { - /* log_error("input parameters illegal!"); */ + fprintf(stderr, "%s input parameters illegal!", __func__); return NULL; } @@ -199,7 +196,7 @@ struct adapter_hs *adapter_hs_initialize(int scan_mode, size_t nr_worker_threads for (size_t i = 0; i < n_expr_array; i++) { if (expr_array[i].n_patterns > MAX_EXPR_PATTERN_NUM) { - //log_error("the number of patterns in one expression should less than %d\n", MAX_EXPR_PATTERN_NUM); + fprintf(stderr, "the number of patterns in one expression should less than %d\n", MAX_EXPR_PATTERN_NUM); return NULL; } @@ -209,7 +206,7 @@ struct adapter_hs *adapter_hs_initialize(int scan_mode, size_t nr_worker_threads } else if (expr_array[i].patterns[j].type == PATTERN_TYPE_REG) { regex_pattern_num++; } else { - /* log_error("unknown pattern type: %d\n", expr_array[i].patterns[j].type); */ + fprintf(stderr, "unknown pattern type: %d\n", expr_array[i].patterns[j].type); return NULL; } } @@ -288,10 +285,10 @@ struct adapter_hs *adapter_hs_initialize(int scan_mode, size_t nr_worker_threads /* create bool matcher */ hs_instance->hs_rt->bm = bool_matcher_new(exprs, n_expr_array, &mem_size); if (hs_instance->hs_rt->bm != NULL) { - /* log_info("Adapter_hs module: build bool matcher of %u expressions with %u bytes memory.", - n_expr_array, mem_size); */ + fprintf(stdout, "Adapter_hs module: build bool matcher of %u expressions with %u bytes memory.", + n_expr_array, mem_size); } else { - /* log_error("Adapter_hs module: build bool matcher failed."); */ + fprintf(stderr, "Adapter_hs module: build bool matcher failed."); goto error; } free(exprs); @@ -299,7 +296,6 @@ struct adapter_hs *adapter_hs_initialize(int scan_mode, size_t nr_worker_threads /* build hs database */ ret = adpt_hs_build_database(hs_instance->hs_rt, literal_cd, regex_cd, scan_mode); if (ret < 0) { - //log_error() goto error; } @@ -320,7 +316,6 @@ struct adapter_hs *adapter_hs_initialize(int scan_mode, size_t nr_worker_threads ret = adpt_hs_alloc_scratch(hs_instance->hs_rt, nr_worker_threads, max_patterns_type); if (ret < 0) { - // log_error goto error; } diff --git a/src/inc_internal/adapter_hs.h b/scanner/adapter_hs.h similarity index 98% rename from src/inc_internal/adapter_hs.h rename to scanner/adapter_hs.h index 5986b59..f395a47 100644 --- a/src/inc_internal/adapter_hs.h +++ b/scanner/adapter_hs.h @@ -24,13 +24,14 @@ extern "C" struct adapter_hs; /* scan mode */ -enum { +enum scan_mode { SCAN_MODE_BLOCK = 1, SCAN_MODE_STREAM, + SCAN_MODE_MAX }; /* pattern type: PATTERN_TYPE_STR(pure literal string) or PATTERN_TYPE_REG(regex expression) */ -enum { +enum pattern_type { PATTERN_TYPE_STR = 1, PATTERN_TYPE_REG, }; diff --git a/test/adapter_hs_gtest.cpp b/scanner/adapter_hs_gtest.cpp similarity index 99% rename from test/adapter_hs_gtest.cpp rename to scanner/adapter_hs_gtest.cpp index 520b581..1031f53 100644 --- a/test/adapter_hs_gtest.cpp +++ b/scanner/adapter_hs_gtest.cpp @@ -1,6 +1,6 @@ #include -#include "maat_utils.h" +#include "utils.h" #include "adapter_hs.h" #include "maat_table_schema.h" diff --git a/src/bool_matcher.cpp b/scanner/bool_matcher.cpp similarity index 100% rename from src/bool_matcher.cpp rename to scanner/bool_matcher.cpp diff --git a/src/inc_internal/bool_matcher.h b/scanner/bool_matcher.h similarity index 100% rename from src/inc_internal/bool_matcher.h rename to scanner/bool_matcher.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fe2e6e..29aa90f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,24 +8,33 @@ set(MAAT_FRAME_VERSION ${MAAT_FRAME_MAJOR_VERSION}.${MAAT_FRAME_MINOR_VERSION}.$ message(STATUS "Maat Frame, Version: ${MAAT_FRAME_VERSION}") add_definitions(-fPIC) -set(MAAT_SRC maat_api.cpp bool_matcher.cpp adapter_hs.cpp rcu_hash.cpp maat_garbage_collection.cpp) - +set(MAAT_SRC maat_api.cpp rcu_hash.cpp maat_garbage_collection.cpp maat_config_monitor.cpp maat_rule.cpp + maat_kv.cpp maat_ex_data.cpp maat_table_schema.cpp maat_table_runtime.cpp maat_utils.cpp) + +set(MAAT_SRC_M main.cpp maat_api.cpp rcu_hash.cpp maat_garbage_collection.cpp maat_config_monitor.cpp maat_rule.cpp + maat_kv.cpp maat_ex_data.cpp maat_table_schema.cpp maat_table_runtime.cpp maat_utils.cpp) + +set(LIB_SOURCE_FILES + ${PROJECT_SOURCE_DIR}/deps/cJSON/cJSON.c) + include_directories(${PROJECT_SOURCE_DIR}/include/) include_directories(${PROJECT_SOURCE_DIR}/deps/) +include_directories(${PROJECT_SOURCE_DIR}/scanner) include_directories(${PROJECT_SOURCE_DIR}/src/inc_internal) # Static Library Output -add_library(maat_frame_static STATIC ${MAAT_SRC}) -#add_dependencies(maat_frame_static hyperscan_static hyperscan_runtime_static) +add_library(maat_frame_static STATIC ${MAAT_SRC} ${LIB_SOURCE_FILES}) set_target_properties(maat_frame_static PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(maat_frame_static PROPERTIES OUTPUT_NAME maatframe) set_target_properties(maat_frame_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) -target_link_libraries(maat_frame_static hyperscan_static) -target_link_libraries(maat_frame_static hyperscan_runtime_static) +target_link_libraries(maat_frame_static adapter-static pthread) + +add_executable(main ${MAAT_SRC_M}) +target_link_libraries(main maat_frame_static) # Shared Library Output -#add_library(maat_frame_shared SHARED ${MAAT_SRC}) +#add_library(maat_frame_shared SHARED ${MAAT_SRC} ${LIB_SOURCE_FILES}) #set_target_properties(maat_frame_shared PROPERTIES LINKER_LANGUAGE CXX) #set_target_properties(maat_frame_shared PROPERTIES OUTPUT_NAME maatframe) #set_target_properties(maat_frame_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1) @@ -33,7 +42,6 @@ target_link_libraries(maat_frame_static hyperscan_runtime_static) #set_target_properties(maat_frame_shared PROPERTIES SOVERSION ${MAAT_FRAME_MAJOR_VERSION}) #target_link_libraries(maat_frame_shared hyperscan_static) -#target_link_libraries(maat_frame_shared hyperscan_runtime_static) # install set(CMAKE_INSTALL_PREFIX /opt/MESA/) diff --git a/src/inc_internal/maat_common.h b/src/inc_internal/maat_common.h index 551b504..872120d 100644 --- a/src/inc_internal/maat_common.h +++ b/src/inc_internal/maat_common.h @@ -20,6 +20,13 @@ extern "C" struct maat_options { size_t nr_worker_threads; + int rule_effect_interval_ms; + int rule_update_checking_interval_ms; + int gc_timeout_ms; + int deferred_load_on; + enum data_source input_mode; + char iris_full_dir[NAME_MAX]; + char iris_inc_dir[NAME_MAX]; }; #ifdef __cpluscplus diff --git a/src/inc_internal/maat_config_monitor.h b/src/inc_internal/maat_config_monitor.h index 30f0a7e..46878ac 100644 --- a/src/inc_internal/maat_config_monitor.h +++ b/src/inc_internal/maat_config_monitor.h @@ -18,15 +18,14 @@ extern "C" #include -#define CONFIG_UPDATE_TYPE_NONE 0 -#define CONFIG_UPDATE_TYPE_FULL 1 -#define CONFIG_UPDATE_TYPE_INC 2 +#define CM_UPDATE_TYPE_NONE 0 +#define CM_UPDATE_TYPE_FULL 1 +#define CM_UPDATE_TYPE_INC 2 - -void config_monitor_traverse(uint64_t version, const char *idx_dir, - void (*pre_fn)(uint64_t, int, void *), +void config_monitor_traverse(long long version, const char *idx_dir, + void (*start_fn)(long long, int, void *), int (*update_fn)(const char *, const char *, void *), - void (*post_fn)(void *), + void (*finish_fn)(void *), void *u_param); #ifdef __cpluscplus diff --git a/src/inc_internal/maat_ex_data.h b/src/inc_internal/maat_ex_data.h index d04c801..7eae24b 100644 --- a/src/inc_internal/maat_ex_data.h +++ b/src/inc_internal/maat_ex_data.h @@ -16,12 +16,47 @@ extern "C" { #endif +#include "rcu_hash.h" + struct ex_data_runtime; -struct ex_data_runtime *ex_data_runtime_new(void (* data_free)(void *data)); +/* ex_data_runtime API */ +struct ex_data_runtime *ex_data_runtime_new(int table_id, rcu_hash_data_free_fn *data_free_fn); void ex_data_runtime_free(struct ex_data_runtime *ex_data_rt); +void ex_data_runtime_commit(struct ex_data_runtime *ex_data_rt); + +/* ex_data_runtime cache row API */ +void ex_data_runtime_cache_row_put(struct ex_data_runtime *ex_data_rt, const char *row); + +const char *ex_data_runtime_cached_row_get(struct ex_data_runtime *ex_data_rt, size_t index); + +size_t ex_data_runtime_cached_row_count(struct ex_data_runtime *ex_data_rt); + +void ex_data_runtime_clear_row_cache(struct ex_data_runtime *ex_data_rt); + +/* set schema API */ +void ex_data_runtime_set_schema(struct ex_data_runtime *ex_data_rt, struct ex_data_schema *schema); + +/* set user_ctx API */ +void ex_data_runtime_set_user_ctx(struct ex_data_runtime *ex_data_rt, void *user_ctx); + +/* ex_data_runtime ex data API */ +void *ex_data_runtime_row2ex_data(struct ex_data_runtime *ex_data_rt, const char *row, const char *key, size_t key_len); + +void ex_data_runtime_add_ex_data(struct ex_data_runtime *ex_data_rt, const char *key, size_t key_len, void *data); + +void ex_data_runtime_del_ex_data(struct ex_data_runtime *ex_data_rt, const char *key, size_t key_len); + +void *ex_data_runtime_get_ex_data(struct ex_data_runtime *ex_data_rt, const char *key, size_t key_len); + +size_t ex_data_runtime_ex_data_count(struct ex_data_runtime *ex_data_rt); + +size_t ex_data_runtime_list_updating_ex_data(struct ex_data_runtime *ex_data_rt, void ***ex_data_array); + +int ex_data_runtime_updating_flag(struct ex_data_runtime *ex_data_rt); + #ifdef __cpluscplus } #endif diff --git a/src/inc_internal/maat_kv.h b/src/inc_internal/maat_kv.h index ab6cc90..7133b2d 100644 --- a/src/inc_internal/maat_kv.h +++ b/src/inc_internal/maat_kv.h @@ -8,8 +8,8 @@ *********************************************************************************************** */ -#ifndef _MAAT_KV_MAP_H_ -#define _MAAT_KV_MAP_H_ +#ifndef _MAAT_KV_H_ +#define _MAAT_KV_H_ #ifdef __cpluscplus extern "C" diff --git a/src/inc_internal/maat_rule.h b/src/inc_internal/maat_rule.h index 7587727..808d95d 100644 --- a/src/inc_internal/maat_rule.h +++ b/src/inc_internal/maat_rule.h @@ -24,49 +24,67 @@ extern "C" struct maat_runtime { /* maat_runtime can be created and destroy dynamic, so need version info */ - uint64_t version; + long long version; time_t last_update_time; - struct maat_table_runtime_manager *table_rt_mgr; + struct table_runtime_manager *table_rt_mgr; size_t max_table_num; - int max_thread_num; + size_t max_thread_num; uint32_t rule_num; }; -enum rule_import_type { - RULE_IMPORT_TYPE_IRIS = 1, - RULE_IMPORT_TYPE_MAX +enum data_source { + DATA_SOURCE_NONE = 0, + DATA_SOURCE_IRIS_FILE }; -struct rule_import_iris_ctx { +struct source_iris_ctx { char inc_dir[NAME_MAX]; char full_dir[NAME_MAX]; }; struct maat { - struct maat_runtime *maat_rt; - struct maat_runtime *rebuilding_maat_rt; //TODO: creating - //struct maat_garbage_collector *gc; - struct maat_table_manager *table_mgr; + char instance_name[NAME_MAX]; - enum rule_import_type rule_import_type; + struct maat_runtime *maat_rt; + struct maat_runtime *creating_maat_rt; + + struct table_schema_manager *table_schema_mgr; + + enum data_source input_mode; union { - struct rule_import_iris_ctx iris_ctx; + struct source_iris_ctx iris_ctx; }; + int deferred_load; + int is_running; pthread_mutex_t background_update_mutex; int nr_worker_thread; - uint64_t maat_version; - uint64_t last_full_version; + long long maat_version; + long long last_full_version; pthread_t cfg_mon_thread; + + int rule_effect_interval_ms; + int rule_update_checking_interval_ms; + int gc_timeout_ms; //garbage collection timeout_ms; + + struct maat_garbage_bin *garbage_bin; }; +void maat_start_cb(long long new_version, int update_type, void *u_para); + +int maat_update_cb(const char *table_name, const char *line, void *u_para); + +void maat_finish_cb(void *u_para); + void *rule_monitor_loop(void *arg); +void maat_read_full_config(struct maat *maat_instance); + #ifdef __cpluscplus } #endif diff --git a/src/inc_internal/maat_table_runtime.h b/src/inc_internal/maat_table_runtime.h index d29ed86..f665af1 100644 --- a/src/inc_internal/maat_table_runtime.h +++ b/src/inc_internal/maat_table_runtime.h @@ -19,19 +19,51 @@ extern "C" #include "maat_table_schema.h" #include "maat_garbage_collection.h" -struct maat_table_item; -struct maat_table_runtime; -struct maat_table_runtime_manager; +struct table_item; +struct table_runtime; +struct table_runtime_manager; -struct maat_table_runtime_manager *maat_table_runtime_manager_create(struct maat_table_manager *table_mgr, int max_thread_num, struct maat_garbage_bin* bin); +/* table runtime manager API */ +struct table_runtime_manager *table_runtime_manager_create(struct table_schema_manager *table_schema_mgr, int max_thread_num, + struct maat_garbage_bin *bin); -void maat_table_runtime_manager_destroy(struct maat_table_runtime_manager *table_rt_mgr); +void table_runtime_manager_destroy(struct table_runtime_manager *table_rt_mgr); -struct maat_table_runtime *maat_table_runtime_get(struct maat_table_runtime_manager *table_rt_mgr, int table_id); +/* table runtime API */ +struct table_runtime *table_runtime_get(struct table_runtime_manager *table_rt_mgr, int table_id); -enum maat_table_type maat_table_runtime_get_type(struct maat_table_runtime* table_rt); +size_t table_runtime_rule_count(struct table_runtime *table_rt); -void maat_table_runtime_item_add(struct maat_table_runtime *table_rt, struct maat_table_item *table_item); +enum table_type table_runtime_get_type(struct table_runtime* table_rt); + +void table_runtime_update(struct table_runtime *table_rt, struct table_schema *table_schema, const char *line, struct table_item *table_item); + +/** + * @brief if table_runtime is updating + * + * @retval 1(yes) 0(no) +*/ +int table_runtime_updating_flag(struct table_runtime *table_rt); + +void table_runtime_commit(struct table_runtime *table_rt, size_t nr_worker_thread); + +/* table runtime scan API */ +int table_runtime_scan_string(struct table_runtime *table_rt, int thread_id, const char *data, size_t data_len, + int results[], size_t *n_result); + +void table_runtime_stream_open(struct table_runtime *table_rt, int thread_id); +int table_runtime_scan_stream(struct table_runtime *table_rt, const char *data, size_t data_len, + int result[], size_t n_result); +void table_runtime_stream_close(struct table_runtime *table_rt); + +/* plugin table runtime API */ +size_t plugin_table_runtime_cached_row_count(struct table_runtime *table_rt); + +const char* plugin_table_runtime_get_cached_row(struct table_runtime *table_rt, size_t row_seq); + +void *plugin_table_runtime_get_ex_data(struct table_runtime *table_rt, struct table_schema *table_schema, const char *key, size_t key_len); + +void plugin_table_runtime_commit_ex_data_schema(struct table_runtime *table_rt, struct table_schema *table_schema); #ifdef __cpluscplus } diff --git a/src/inc_internal/maat_table_schema.h b/src/inc_internal/maat_table_schema.h index 68e9d23..440067f 100644 --- a/src/inc_internal/maat_table_schema.h +++ b/src/inc_internal/maat_table_schema.h @@ -18,27 +18,152 @@ extern "C" #include -#include "sds/sds.h" +#include "maat/maat.h" +#include "adapter_hs.h" -enum maat_table_type { +#define MAX_DISTRICT_STR 128 +#define MAX_IP_STR 128 +#define MAX_KEYWORDS_STR 1024 + +enum table_type { TABLE_TYPE_EXPR = 0, + TABLE_TYPE_EXPR_PLUS, + TABLE_TYPE_IP, + TABLE_TYPE_PLUGIN, TABLE_TYPE_IP_PLUGIN, TABLE_TYPE_MAX }; -struct maat_table_schema; -struct maat_table_manager; -struct maat_table_item; +enum expr_type { + EXPR_TYPE_STRING = 1, + EXPR_TYPE_REGEX, + EXPR_TYPE_AND, + EXPR_TYPE_OFFSET, + EXPR_TYPE_MAX +}; -struct maat_table_manager *maat_table_manager_create(sds table_info_path); -void maat_table_manager_destroy(struct maat_table_manager *table_mgr); +enum match_method { + MATCH_METHOD_SUB=0, + MATCH_METHOD_RIGHT, + MATCH_METHOD_LEFT, + MATCH_METHOD_COMPLETE, + MATCH_METHOD_MAX +}; -int maat_table_manager_get_table_id(struct maat_table_manager* table_mgr, sds table_name); -enum maat_table_type maat_table_manager_get_table_type(struct maat_table_manager *table_mgr, int id); +struct expr_item { + int item_id; + int group_id; + char district[MAX_DISTRICT_STR]; + char keywords[MAX_KEYWORDS_STR]; + enum expr_type expr_type; + enum match_method match_method; + int is_hexbin; + int is_case_sensitive; + int is_valid; + + //rule_tag; 只存在schema里 + //int have_exdata; + //struct ex_data *ex_data; //hash表 +}; -size_t maat_table_manager_get_size(struct maat_table_manager* table_mgr); +struct plugin_item { + char key[MAX_KEYWORDS_STR]; + size_t key_len; + int is_valid; +}; -struct maat_table_item *maat_table_line_to_item(sds line, struct maat_table_schema *table_schema); +struct ip_plugin_item { + int item_id; + int ip_type; + char start_ip[MAX_IP_STR]; + char end_ip[MAX_IP_STR]; + int is_valid; + int rule_tag; + int have_exdata; + void *ex_data; +}; + +struct table_item { + enum table_type table_type; + union { + struct expr_item expr_item; + struct plugin_item plugin_item; + struct ip_plugin_item ip_plugin_item; + }; +}; + +struct plugin_table_callback_schema +{ + maat_start_callback_t *start; + maat_update_callback_t *update; + maat_finish_callback_t *finish; + void* u_para; +}; + +struct ex_data_schema +{ + maat_plugin_ex_new_func_t *new_func; + maat_plugin_ex_free_func_t *free_func; + maat_plugin_ex_dup_func_t *dup_func; + //Maat_plugin_EX_key2index_func_t* key2index_func; + long argl; + void *argp; + int set_flag; +}; + +struct table_schema; +struct table_schema_manager; + +/* table schema manager API */ +struct table_schema_manager *table_schema_manager_create(const char *table_info_path); +void table_schema_manager_destroy(struct table_schema_manager *table_schema_mgr); + +int table_schema_manager_get_table_id(struct table_schema_manager* table_schema_mgr, const char *table_name); +enum table_type table_schema_manager_get_table_type(struct table_schema_manager *table_schema_mgr, int table_id); + +size_t table_schema_manager_get_size(struct table_schema_manager* table_schema_mgr); + +void table_schema_manager_all_plugin_cb_start(struct table_schema_manager* table_schema_mgr, int update_type); +void table_schema_manager_all_plugin_cb_finish(struct table_schema_manager* table_schema_mgr); + +/* table schema generic API */ +struct table_schema *table_schema_get(struct table_schema_manager *table_schema_mgr, int table_id); + +enum table_type table_schema_get_table_type(struct table_schema *table_schema); + +int table_schema_get_table_id(struct table_schema *table_schema); + +struct table_item *table_schema_line_to_item(const char *line, struct table_schema *table_schema); + +/* expr table schema API */ +enum scan_mode expr_table_schema_get_scan_mode(struct table_schema *table_schema); + +/* plugin table schema API */ +int plugin_table_schema_set_ex_data_schema(struct table_schema *table_schema, + maat_plugin_ex_new_func_t *new_func, + maat_plugin_ex_free_func_t *free_func, + maat_plugin_ex_dup_func_t *dup_func, + long argl, void *argp); + +struct ex_data_schema *plugin_table_schema_get_ex_data_schema(struct table_schema *table_schema); +/** + * @brief if plugin table schema's ex data schema set + * + * @retval 1(already Set) 0(Not set yet) +*/ +int plugin_table_schema_ex_data_schema_flag(struct table_schema *table_schema); + +int plugin_table_schema_add_callback(struct table_schema_manager* table_schema_mgr, int table_id, + maat_start_callback_t *start,//MAAT_RULE_UPDATE_TYPE_*,u_para + maat_update_callback_t *update,//table line ,u_para + maat_finish_callback_t *finish,//u_para + void *u_para); +/** + * @brief the number of callback function stored in plugin table schema +*/ +size_t plugin_table_schema_callback_count(struct table_schema *table_schema); + +void plugin_table_schema_all_cb_update(struct table_schema *table_schema, const char *row); #ifdef __cpluscplus } diff --git a/src/inc_internal/maat_utils.h b/src/inc_internal/maat_utils.h index 0a08879..4ef044b 100644 --- a/src/inc_internal/maat_utils.h +++ b/src/inc_internal/maat_utils.h @@ -19,11 +19,28 @@ extern "C" #include #include -#include "sds/sds.h" +#define TRUE 1 +#define FALSE 0 -#define ALLOC(type, number) ((type *)calloc(sizeof(type), number)) +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif -int get_column_pos(sds line, int column_seq, size_t *offset, size_t *len); +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +char *maat_strdup(const char *s); + +int get_column_pos(const char *line, int column_seq, size_t *offset, size_t *len); + +int load_file_to_memory(const char *file_name, unsigned char **pp_out, size_t *out_sz); + +char *strtok_r_esc(char *s, const char delim, char **save_ptr); + +char *str_unescape_and(char *s); + +char *str_unescape(char *s); #ifdef __cpluscplus } diff --git a/src/inc_internal/rcu_hash.h b/src/inc_internal/rcu_hash.h index 6482e34..ee22a6f 100644 --- a/src/inc_internal/rcu_hash.h +++ b/src/inc_internal/rcu_hash.h @@ -18,16 +18,20 @@ extern "C" #include "uthash/uthash.h" +typedef void rcu_hash_data_free_fn(void *user_ctx, void *data); + /* rcu hash table */ struct rcu_hash_table; -struct rcu_hash_table *rcu_hash_new(void (* data_free)(void *data)); +struct rcu_hash_table *rcu_hash_new(rcu_hash_data_free_fn *free_fn); void rcu_hash_free(struct rcu_hash_table *htable); +void rcu_hash_set_user_ctx(struct rcu_hash_table *htable, void *user_ctx); + /** - * @brief the data added just in updating stage - * after call rcu_hash_commit, it in effective stage + * @brief just means add to the updating nodes + * after call rcu_hash_commit, they become effective nodes */ void rcu_hash_add(struct rcu_hash_table *htable, const char *key, size_t key_len, void *data); @@ -44,14 +48,24 @@ void rcu_hash_del(struct rcu_hash_table *htable, const char *key, size_t key_len */ void *rcu_hash_find(struct rcu_hash_table *htable, const char *key, size_t key_len); -size_t rcu_hash_counts(struct rcu_hash_table *htable); - +size_t rcu_hash_count(struct rcu_hash_table *htable); /** * @brief make add/del effective */ void rcu_hash_commit(struct rcu_hash_table *htable); +size_t rcu_hash_garbage_queue_len(struct rcu_hash_table *htable); + +size_t rcu_hash_list_updating_data(struct rcu_hash_table *htable, void ***data_array); + +/** + * @brief check if rcu hash table is updating + * + * @retval 1 means htable is updating, otherwise 0 +*/ +int rcu_hash_updating_flag(struct rcu_hash_table *htable); + #ifdef __cpluscplus } #endif diff --git a/src/maat_api.cpp b/src/maat_api.cpp index 373dd6a..9dcb0ce 100644 --- a/src/maat_api.cpp +++ b/src/maat_api.cpp @@ -9,19 +9,27 @@ */ #include +#include -#include "maat_utils.h" +#include "utils.h" +#include "maat/maat.h" #include "maat_rule.h" #include "maat_common.h" +#include "maat_kv.h" #include "maat_table_schema.h" #include "maat_table_runtime.h" -#include "sds/sds.h" +#include "maat_config_monitor.h" struct maat_options* maat_options_new(void) { struct maat_options *options = ALLOC(struct maat_options, 1); options->nr_worker_threads = 1; + options->deferred_load_on = 0; + options->rule_effect_interval_ms = 60 * 1000; + options->rule_update_checking_interval_ms = 1 * 1000; + options->gc_timeout_ms = 10 * 1000; + options->input_mode = DATA_SOURCE_NONE; return options; } @@ -33,25 +41,129 @@ int maat_options_set_worker_thread_number(struct maat_options *opts, size_t n_wo return 0; } -struct maat *maat_new(struct maat_options opts, const char *table_info_path) +int maat_options_set_rule_effect_interval_ms(struct maat_options *opts, int interval_ms) +{ + opts->rule_effect_interval_ms = interval_ms; + + return 0; +} + +int maat_options_set_rule_update_checking_interval_ms(struct maat_options *opts, int interval_ms) +{ + opts->rule_update_checking_interval_ms = interval_ms; + + return 0; +} + +int maat_options_set_gc_timeout_ms(struct maat_options *opts, int interval_ms) +{ + opts->gc_timeout_ms = interval_ms; + + return 0; +} + +int maat_options_set_deferred_load_on(struct maat_options *opts) +{ + opts->deferred_load_on = 1; + return 0; +} + +int maat_options_set_iris_full_dir(struct maat_options *opts, const char *full_dir) +{ + if (strlen(full_dir) >= NAME_MAX) { + return -1; + } + + memcpy(opts->iris_full_dir, full_dir, strlen(full_dir)); + opts->input_mode = DATA_SOURCE_IRIS_FILE; + return 0; +} + +int maat_options_set_iris_inc_dir(struct maat_options *opts, const char *inc_dir) +{ + if (strlen(inc_dir) >= NAME_MAX) { + return -1; + } + + memcpy(opts->iris_inc_dir, inc_dir, strlen(inc_dir)); + opts->input_mode = DATA_SOURCE_IRIS_FILE; + return 0; +} + +void maat_read_full_config(struct maat *maat_instance) +{ + switch (maat_instance->input_mode) { + case DATA_SOURCE_IRIS_FILE: + config_monitor_traverse(maat_instance->maat_version, + maat_instance->iris_ctx.full_dir, + maat_start_cb, + maat_update_cb, + maat_finish_cb, + maat_instance); + if (NULL == maat_instance->creating_maat_rt) { + fprintf(stderr, "At initiation: NO effective rule in %s.", + maat_instance->iris_ctx.full_dir); + } + break; + default: + break; + } + + maat_instance->maat_rt = maat_instance->creating_maat_rt; + maat_instance->creating_maat_rt = NULL; + maat_instance->is_running = 1; + if (maat_instance->maat_rt != NULL) { + maat_instance->maat_version = maat_instance->maat_rt->version; + maat_instance->last_full_version = maat_instance->maat_rt->version; + } +} + +struct maat *maat_new(struct maat_options *opts, const char *table_info_path) { - #if 0 if (NULL == table_info_path) { return NULL; } + int garbage_gc_timeout_s = 0; struct maat *maat_instance = ALLOC(struct maat, 1); - maat_instance->table_mgr = maat_table_manager_create(table_info_path); + maat_instance->table_schema_mgr = table_schema_manager_create(table_info_path); + if (NULL == maat_instance->table_schema_mgr) { + goto failed; + } + + maat_instance->input_mode = opts->input_mode; + switch (maat_instance->input_mode) { + case DATA_SOURCE_IRIS_FILE: + memcpy(maat_instance->iris_ctx.full_dir, opts->iris_full_dir, strlen(opts->iris_full_dir)); + memcpy(maat_instance->iris_ctx.inc_dir, opts->iris_inc_dir, strlen(opts->iris_inc_dir)); + break; + default: + fprintf(stderr, "data source unsupported:%d\n", maat_instance->input_mode); + goto failed; + } + maat_instance->is_running = 0; maat_instance->maat_version = 0; maat_instance->last_full_version = 0; - maat_instance->nr_worker_thread = opts.nr_worker_threads; + maat_instance->nr_worker_thread = opts->nr_worker_threads; + maat_instance->rule_effect_interval_ms = opts->rule_effect_interval_ms; + maat_instance->gc_timeout_ms = opts->gc_timeout_ms; + maat_instance->deferred_load = opts->deferred_load_on; + garbage_gc_timeout_s = (maat_instance->rule_effect_interval_ms / 1000) + + (maat_instance->gc_timeout_ms / 1000); + maat_instance->garbage_bin = maat_garbage_bin_new(garbage_gc_timeout_s); + pthread_mutex_init(&(maat_instance->background_update_mutex), NULL); + + if (0 == maat_instance->deferred_load) { + maat_read_full_config(maat_instance); + } pthread_create(&(maat_instance->cfg_mon_thread), NULL, rule_monitor_loop, (void*)maat_instance); return maat_instance; - #endif +failed: + free(maat_instance); return NULL; } @@ -64,11 +176,100 @@ void maat_free(struct maat *maat_instance) return; } -int maat_table_get_id(struct maat *instance, const char *table_name) +int maat_table_get_id(struct maat *maat_instance, const char *table_name) { + int table_id = -1; + + struct table_schema_manager *table_schema_mgr = maat_instance->table_schema_mgr; + table_id = table_schema_manager_get_table_id(table_schema_mgr, table_name); + + return table_id; +} + +int maat_table_callback_register(struct maat *maat_instance, int table_id, + maat_start_callback_t *start, + maat_update_callback_t *update, + maat_finish_callback_t *finish, + void *u_para) +{ + int ret = -1; + + pthread_mutex_lock(&(maat_instance->background_update_mutex)); + ret = plugin_table_schema_add_callback(maat_instance->table_schema_mgr, table_id, start, update, finish, u_para); + if (ret < 0) { + pthread_mutex_unlock(&(maat_instance->background_update_mutex)); + return -1; + } + + if (!maat_instance->maat_rt) { + pthread_mutex_unlock(&(maat_instance->background_update_mutex)); + return 0; + } + + struct table_runtime *table_rt = table_runtime_get(maat_instance->maat_rt->table_rt_mgr, table_id); + size_t row_count = plugin_table_runtime_cached_row_count(table_rt); + if (row_count > 0) { + if (start != NULL) { + start(MAAT_RULE_UPDATE_TYPE_FULL, u_para); + } + + for (size_t i = 0; i < row_count; i++) { + const char *line = plugin_table_runtime_get_cached_row(table_rt, i); + if (NULL == line) { + break; + } + + update(table_id, line, u_para); + } + + if (finish != NULL) { + finish(u_para); + } + } + + pthread_mutex_unlock(&(maat_instance->background_update_mutex)); + return 0; } +int maat_plugin_table_ex_schema_register(struct maat *maat_instance, int table_id, + maat_plugin_ex_new_func_t *new_func, + maat_plugin_ex_free_func_t *free_func, + maat_plugin_ex_dup_func_t *dup_func, + long argl, void *argp) +{ + struct table_schema *table_schema = table_schema_get(maat_instance->table_schema_mgr, table_id); + pthread_mutex_lock(&(maat_instance->background_update_mutex)); + int ret = plugin_table_schema_set_ex_data_schema(table_schema, new_func, free_func, dup_func, argl, argp); + if (ret < 0) { + pthread_mutex_unlock(&(maat_instance->background_update_mutex)); + return -1; + } + + struct table_runtime *table_rt = NULL; + if (maat_instance->maat_rt != NULL) { + table_rt = table_runtime_get(maat_instance->maat_rt->table_rt_mgr, table_id); + plugin_table_runtime_commit_ex_data_schema(table_rt, table_schema); + } + pthread_mutex_unlock(&(maat_instance->background_update_mutex)); + + return 0; +} + +void *maat_plugin_table_get_ex_data(struct maat *maat_instance, int table_id, + const char *key, size_t key_len) +{ + struct maat_runtime *maat_rt = maat_instance->maat_rt; + if (NULL == maat_rt) { + return NULL; + } + + struct table_schema *table_schema = table_schema_get(maat_instance->table_schema_mgr, table_id); + struct table_runtime *table_rt = table_runtime_get(maat_rt->table_rt_mgr, table_id); + + return plugin_table_runtime_get_ex_data(table_rt, table_schema, key, key_len); +} + int maat_scan_integer(struct maat *instance, int table_id, int thread_id, unsigned int intval, int results[], size_t n_result, struct maat_state *state) @@ -87,16 +288,10 @@ int maat_scan_string(struct maat *maat_instance, int table_id, int thread_id, const char *data, size_t data_len, int results[], size_t *n_results, struct maat_state *state) { - #if 0 - if (NULL == maat_instance || NULL == data ) { - return -1; - } + struct table_runtime_manager *table_rt_mgr = maat_instance->maat_rt->table_rt_mgr; + struct table_runtime *table_rt = table_runtime_get(table_rt_mgr, table_id); - struct maat_runtime *maat_rt = maat_instance->maat_rt; - - struct maat_table_runtime *table_rt = maat_table_runtime_get(maat_rt->table_rt_mgr, table_id); - #endif - return 0; + return table_runtime_scan_string(table_rt, thread_id, data, data_len, results, n_results); } struct maat_stream *maat_scan_stream_open(struct maat *instance, int table_id, int thread_id) diff --git a/src/maat_config_monitor.cpp b/src/maat_config_monitor.cpp index 82dbef3..69c5862 100644 --- a/src/maat_config_monitor.cpp +++ b/src/maat_config_monitor.cpp @@ -13,9 +13,11 @@ #include #include #include +#include #include "maat_config_monitor.h" #include "maat_utils.h" +#include "utils.h" #define CM_MAX_TABLE_NUM 256 #define MAX_CONFIG_LINE (1024 * 16) @@ -28,17 +30,6 @@ struct cm_table_info_t char encryp_algorithm[NAME_MAX]; }; -/* - @brief check if rule file updated - @retval 0 -> CONFIG_UPDATE_TYPE_NONE - 1 -> CONFIG_UPDATE_TYPE_FULL - 2 -> CONFIG_UPDATE_TYPE_INC -*/ -int validate_update_happened(uint64_t current_version, const char *idx_dir, char **idx_paths[], size_t *n_idx_paths) -{ - -} - int cm_read_cfg_index_file(const char* path, struct cm_table_info_t* idx, int size) { int ret = 0; @@ -55,7 +46,7 @@ int cm_read_cfg_index_file(const char* path, struct cm_table_info_t* idx, int si ,idx[i].cfg_path ,idx[i].encryp_algorithm); //jump over empty line - if (!(ret==3||ret==4)||idx[i].cfg_num==0){ + if (!(ret == 3 || ret == 4) || idx[i].cfg_num == 0) { continue; } @@ -77,17 +68,15 @@ int cm_read_cfg_index_file(const char* path, struct cm_table_info_t* idx, int si return i; } -const char* path2filename(const char*path) +const char *path2filename(const char *path) { - int i=0; - for(i=strlen(path);i>0;i--) - { - if(path[i]=='/') - { + int i = 0; + for (i = strlen(path);i > 0;i--) { + if (path[i]=='/') { break; } } - return path+i+1; + return path + i + 1; } char *read_nxt_line_from_buff(const char *buff, size_t buff_size, size_t *offset, char *line, int line_size) @@ -96,9 +85,9 @@ char *read_nxt_line_from_buff(const char *buff, size_t buff_size, size_t *offset const char* p; //search for CRLF, aka CR '\r'(old Mac), LF '\n'(UNIX) or CRLF"\r\n" (Windows) - p = (const char*)memchr(buff+*offset, '\n', buff_size-*offset); + p = (const char *)memchr(buff + *offset, '\n', buff_size - *offset); if (p == NULL) { // NOT "\n" or "\r\n" - p = (const char*)memchr(buff+*offset, '\r', buff_size-*offset); + p = (const char *)memchr(buff + *offset, '\r', buff_size - *offset); } if (p != NULL) { //point to next character @@ -108,7 +97,7 @@ char *read_nxt_line_from_buff(const char *buff, size_t buff_size, size_t *offset } this_offset = p - (buff + *offset); - memcpy(line, buff + *offset, MIN(this_offset, line_size-1)); + memcpy(line, buff + *offset, MIN(this_offset, line_size - 1)); *offset += this_offset; line[MIN(this_offset, line_size - 1)] = '\0'; @@ -122,7 +111,6 @@ int cm_read_table_file(struct cm_table_info_t* index, { int cfg_num = 0,i =0; int ret = 0; - char error_string[NAME_MAX]; char line[MAX_CONFIG_LINE]={0}; char *ret_str=NULL; char *table_file_buff=NULL; @@ -144,16 +132,15 @@ int cm_read_table_file(struct cm_table_info_t* index, } for (i = 0; i < cfg_num; i++) { - line[sizeof(line)-1]='\0'; + line[sizeof(line) - 1] = '\0'; - ret_str=read_nxt_line_from_buff(table_file_buff, file_sz, &file_offset, line, sizeof(line)); - - if(ret_str==NULL) { + ret_str = read_nxt_line_from_buff(table_file_buff, file_sz, &file_offset, line, sizeof(line)); + if (ret_str == NULL) { //log_error break; } - if(line[sizeof(line)-1]!='\0') { + if(line[sizeof(line) - 1] != '\0') { //log_error continue; } @@ -169,32 +156,174 @@ int cm_read_table_file(struct cm_table_info_t* index, return 0; } -void config_monitor_traverse(uint64_t current_version, const char *idx_dir, - void (*start_fn)(uint64_t, int, void *), +//replacement of glibc scandir, to adapt dictator malloc wrap +#define ENLARGE_STEP 1024 +int my_scandir(const char *dir, struct dirent ***namelist, + int(*filter)(const struct dirent *), + int(*compar)(const void *, const void *)) +{ + int n = 0; + int DIR_ENT_SIZE = ENLARGE_STEP; + struct dirent entry, *result; + + if ((NULL == dir) || (NULL == namelist)) { + return -1; + } + + DIR *od = opendir(dir); + if (NULL == od) { + return -1; + } + + struct dirent **list = ALLOC(struct dirent *, DIR_ENT_SIZE); + while (0 == readdir_r(od, &entry, &result)) { + if (NULL == result) { + break; + } + + if (filter && !filter(&entry)) { + continue; + } + + struct dirent *p = ALLOC(struct dirent, 1); + memcpy((void *)p, (void *)(&entry), sizeof(struct dirent)); + list[n] = p; + + n++; + if (n >= DIR_ENT_SIZE) { + DIR_ENT_SIZE += ENLARGE_STEP; + list = (struct dirent **)realloc((void*)list, DIR_ENT_SIZE * sizeof(struct dirent *)); + } + } + + closedir(od); + *namelist = list; + + if (compar) { + qsort((void *)*namelist, n, sizeof(struct dirent *), compar); + } + + return n; +} + +int filter_fn(const struct dirent *ent) +{ + return (strncmp(ent->d_name,"full_config_index",strlen("full_config_index")) == 0 || + strncmp(ent->d_name,"inc_config_index",strlen("inc_config_index")) == 0); +} + +int get_new_idx_path(long long current_version, const char *file_dir, char ***idx_path, size_t *idx_num) +{ + struct dirent **namelist = NULL; + int update_type = CM_UPDATE_TYPE_NONE; + + int n = my_scandir(file_dir, &namelist, filter_fn, (int (*)(const void*, const void*))alphasort); + if (n < 0) { + //log_error("scan dir error"); + return update_type; + } + + long long latest_full_version = 0; + long long latest_inc_version = 0; + int full_file_idx = 0; + int *inc_file_idx = ALLOC(int, n); + int inc_idx_num = 0; + int i = 0; + for (i = 0; i < n; i++) { + long long config_seq = 0; + char update_str[32] = {0}; + + if ((strcmp(namelist[i]->d_name, ".") == 0) || (strcmp(namelist[i]->d_name, "..") == 0)) { + continue; + } + + if (strlen(namelist[i]->d_name) > 42) { + //log_error("config file %s filename too long,should like full_config_index.00000000000000000001",namelist[i]->d_name); + continue; + } + + int ret = sscanf(namelist[i]->d_name,"%[a-zA-Z]_config_index.%lld", update_str, &config_seq); + if (ret != 2) { + //log_error("config file %s filename error,should like full_config_index.00000000000000000001",namelist[i]->d_name); + continue; + } + + if (strncasecmp(update_str, "full", strlen(update_str)) == 0) { + if (config_seq > latest_full_version) { + latest_full_version = config_seq; + full_file_idx = i; + } + } else if(strncasecmp(update_str,"inc",strlen(update_str))==0) { + if (config_seq > current_version) { + inc_file_idx[inc_idx_num] = i; + inc_idx_num++; + if (config_seq > latest_inc_version) { + latest_inc_version = config_seq; + } + } + } else { + //log_error("config file %s,not full or inc config",namelist[i]->d_name); + } + } + + size_t path_len = 0; + //full update + if (latest_full_version > current_version) { + *idx_path = (char **)malloc(sizeof(char **)); + path_len = strlen(file_dir) + strlen(namelist[full_file_idx]->d_name) + 1 + 1; + (*idx_path)[0] = (char *)malloc(path_len); + snprintf((*idx_path)[0], path_len, "%s/%s", file_dir, namelist[full_file_idx]->d_name); + *idx_num = 1; + update_type = CM_UPDATE_TYPE_FULL; + } else if (latest_inc_version > current_version) { + //inc update,it's possible that do inc after full update in this function,but we'll process it at next loop. + *idx_path = (char **)malloc(sizeof(char **) * inc_idx_num); + for (i = 0; i < inc_idx_num; i++) { + path_len = strlen(file_dir) + strlen(namelist[inc_file_idx[i]]->d_name) + 1 + 1; + (*idx_path)[i] = (char *)malloc(path_len); + snprintf((*idx_path)[i], path_len, "%s/%s", file_dir, namelist[inc_file_idx[i]]->d_name); + } + *idx_num = inc_idx_num; + update_type = CM_UPDATE_TYPE_INC; + } else { + update_type = CM_UPDATE_TYPE_NONE; + } + + free(inc_file_idx); + for (i = 0; i < n; i++) { + free(namelist[i]); + } + free(namelist); + + return update_type; +} + +void config_monitor_traverse(long long current_version, const char *idx_dir, + void (*start_fn)(long long, int, void *), int (*update_fn)(const char *, const char *, void *), void (*finish_fn)(void *), void *u_param) { size_t i = 0; - uint64_t new_version = 0; + long long new_version = 0; char **idx_path_array = NULL; size_t idx_path_num = 0; struct cm_table_info_t table_array[CM_MAX_TABLE_NUM]; memset(table_array, 0, sizeof(table_array)); - int update_type = validate_update_happened(current_version, idx_dir, &idx_path_array, &idx_path_num); - if (update_type != CONFIG_UPDATE_TYPE_NONE) { + int update_type = get_new_idx_path(current_version, idx_dir, &idx_path_array, &idx_path_num); + if (update_type != CM_UPDATE_TYPE_NONE) { for (i = 0; i < idx_path_num; i++) { int table_num = cm_read_cfg_index_file(idx_path_array[i], table_array, CM_MAX_TABLE_NUM); if (table_num < 0) { - //log_error + //log_error luis break; } char str_not_care[256] = {0}; - const char* table_filename = path2filename(idx_path_array[i]); - sscanf(table_filename,"%[a-zA-Z]_config_index.%lld", str_not_care, &new_version); + const char *table_filename = path2filename(idx_path_array[i]); + sscanf(table_filename, "%[a-zA-Z]_config_index.%lld", str_not_care, &new_version); if (start_fn != NULL) { start_fn(new_version, update_type, u_param); } diff --git a/src/maat_ex_data.cpp b/src/maat_ex_data.cpp index 19b1036..2211e19 100644 --- a/src/maat_ex_data.cpp +++ b/src/maat_ex_data.cpp @@ -9,53 +9,50 @@ */ #include +#include #include "uthash/uthash.h" #include "uthash/utarray.h" -#include "maat_rhash.h" +#include "rcu_hash.h" +#include "utils.h" #include "maat_utils.h" - -typedef void ex_data_schema_new_func_t(int table_id, const char *key, const char *table_line, void* ad, long argl, void *argp); -typedef void ex_data_schema_free_func_t(int table_id, void* ad, long argl, void *argp); -typedef void ex_data_schema_dup_func_t(int table_id, void *to, void *from, long argl, void *argp); -/* -struct ex_data_schema -{ - ex_data_schema_new_func_t* new_func; - ex_data_schema_free_func_t* free_func; - ex_data_schema_dup_func_t* dup_func; - //Maat_plugin_EX_key2index_func_t* key2index_func; - long argl; - void *argp; -}; */ +#include "maat_table_schema.h" +#include "maat_ex_data.h" struct ex_data_runtime { UT_array *cache_rows; size_t cache_row_num; size_t cache_size; - struct rhash_table *htable; - //const struct ex_data_schema* ex_schema; + struct rcu_hash_table *htable; + struct ex_data_schema *ex_schema; int table_id; }; -void cache_row_free(void*p) +void cache_row_free(void *p) { - free(*(char**)p); + free(*(char **)p); } UT_icd ut_cache_row_icd = {sizeof(char*), NULL, NULL, cache_row_free}; -struct ex_data_runtime *ex_data_runtime_new(void (* data_free)(void *data)) +struct ex_data_runtime *ex_data_runtime_new(int table_id, rcu_hash_data_free_fn *data_free_fn) { struct ex_data_runtime *ex_data_rt = ALLOC(struct ex_data_runtime, 1); utarray_new(ex_data_rt->cache_rows, &ut_cache_row_icd); - ex_data_rt->htable = rhash_table_create(data_free); + ex_data_rt->htable = rcu_hash_new(data_free_fn); + ex_data_rt->table_id = table_id; + + return ex_data_rt; } void ex_data_runtime_free(struct ex_data_runtime *ex_data_rt) { + if (NULL == ex_data_rt) { + return; + } + if (ex_data_rt->cache_rows != NULL) { utarray_free(ex_data_rt->cache_rows); ex_data_rt->cache_rows=NULL; @@ -63,8 +60,128 @@ void ex_data_runtime_free(struct ex_data_runtime *ex_data_rt) } if (ex_data_rt->htable != NULL) { - rhash_table_destroy(ex_data_rt->htable); + rcu_hash_free(ex_data_rt->htable); } free(ex_data_rt); +} + +void ex_data_runtime_commit(struct ex_data_runtime *ex_data_rt) +{ + rcu_hash_commit(ex_data_rt->htable); +} + +void ex_data_runtime_cache_row_put(struct ex_data_runtime *ex_data_rt, const char *row) +{ + size_t len = strlen(row) + 1; + char* row_copy = ALLOC(char, len); + + memcpy(row_copy, row, len); + ex_data_rt->cache_size += len; + utarray_push_back(ex_data_rt->cache_rows, &row_copy); + ex_data_rt->cache_row_num++; +} + +const char *ex_data_runtime_cached_row_get(struct ex_data_runtime *ex_data_rt, size_t index) +{ + const char** row = NULL; + row = (const char **)utarray_eltptr(ex_data_rt->cache_rows, index); + return *row; +} + +void ex_data_runtime_clear_cached_row(struct ex_data_runtime *ex_data_rt) +{ + utarray_free(ex_data_rt->cache_rows); + ex_data_rt->cache_rows = NULL; + ex_data_rt->cache_row_num = 0; + ex_data_rt->cache_size = 0; +} + +size_t ex_data_runtime_cached_row_count(struct ex_data_runtime *ex_data_rt) +{ + if (NULL == ex_data_rt) { + return 0; + } + + return ex_data_rt->cache_row_num; +} + +void ex_data_runtime_clear_row_cache(struct ex_data_runtime *ex_data_rt) +{ + utarray_free(ex_data_rt->cache_rows); + ex_data_rt->cache_rows = NULL; + ex_data_rt->cache_row_num = 0; + ex_data_rt->cache_size = 0; +} + +void ex_data_runtime_set_schema(struct ex_data_runtime *ex_data_rt, struct ex_data_schema *schema) +{ + ex_data_rt->ex_schema = schema; +} + +void ex_data_runtime_set_user_ctx(struct ex_data_runtime *ex_data_rt, void *user_ctx) +{ + rcu_hash_set_user_ctx(ex_data_rt->htable, user_ctx); +} + +void *ex_data_runtime_row2ex_data(struct ex_data_runtime *ex_data_rt, const char *row, const char *key, size_t key_len) +{ + void *ex_data = NULL; + struct ex_data_schema *ex_schema = ex_data_rt->ex_schema; + ex_schema->new_func(ex_data_rt->table_id, key, row, &ex_data, ex_schema->argl, ex_schema->argp); + + return ex_data; +} + +void ex_data_runtime_add_ex_data(struct ex_data_runtime *ex_data_rt, const char *key, size_t key_len, void *data) +{ + void *tmp_data = rcu_hash_find(ex_data_rt->htable, key, key_len); + if (tmp_data != NULL) { + return; + } + + rcu_hash_add(ex_data_rt->htable, key, key_len, data); +} + +void ex_data_runtime_del_ex_data(struct ex_data_runtime *ex_data_rt, const char *key, size_t key_len) +{ + void *tmp_data = rcu_hash_find(ex_data_rt->htable, key, key_len); + if (NULL == tmp_data) { + fprintf(stderr, "ex data del error: no such key:%s", key); + return; + } + + rcu_hash_del(ex_data_rt->htable, key, key_len); +} + +void *ex_data_runtime_get_ex_data(struct ex_data_runtime *ex_data_rt, const char *key, size_t key_len) +{ + void *ex_data = rcu_hash_find(ex_data_rt->htable, key, key_len); + if (NULL == ex_data) { + return NULL; + } + + void *dup_ex_data = NULL; + ex_data_rt->ex_schema->dup_func(ex_data_rt->table_id, &dup_ex_data, &ex_data, + ex_data_rt->ex_schema->argl, ex_data_rt->ex_schema->argp); + return dup_ex_data; +} + +size_t ex_data_runtime_ex_data_count(struct ex_data_runtime *ex_data_rt) +{ + return rcu_hash_count(ex_data_rt->htable); +} + +size_t ex_data_runtime_list_updating_ex_data(struct ex_data_runtime *ex_data_rt, void ***ex_data_array) +{ + if (NULL == ex_data_rt->ex_schema) { + return 0; + } + + return rcu_hash_list_updating_data(ex_data_rt->htable, ex_data_array); +} + +int ex_data_runtime_updating_flag(struct ex_data_runtime *ex_data_rt) +{ + return rcu_hash_updating_flag(ex_data_rt->htable); } \ No newline at end of file diff --git a/src/maat_garbage_collection.cpp b/src/maat_garbage_collection.cpp index 4b6b060..9f491a9 100644 --- a/src/maat_garbage_collection.cpp +++ b/src/maat_garbage_collection.cpp @@ -8,7 +8,7 @@ *********************************************************************************************** */ -#include "maat_utils.h" +#include "utils.h" #include #include diff --git a/src/maat_kv.cpp b/src/maat_kv.cpp index d50f74d..395a5a0 100644 --- a/src/maat_kv.cpp +++ b/src/maat_kv.cpp @@ -12,7 +12,7 @@ #include #include "uthash/uthash.h" -#include "maat_utils.h" +#include "utils.h" #define MAAT_KV_MAX_KEY_LEN 512 diff --git a/src/maat_rule.cpp b/src/maat_rule.cpp index e146d72..fed4857 100644 --- a/src/maat_rule.cpp +++ b/src/maat_rule.cpp @@ -9,92 +9,179 @@ */ #include +#include #include #include +#include +#include +#include #include "maat_rule.h" #include "maat_config_monitor.h" +#include "utils.h" #include "maat_utils.h" #include "maat_table_runtime.h" #include "maat_table_schema.h" -struct maat_runtime* create_maat_runtime(uint64_t version, struct maat *maat_instance) +struct maat_runtime* maat_runtime_create(long long version, struct maat *maat_instance) { struct maat_runtime *maat_rt = ALLOC(struct maat_runtime, 1); maat_rt->version = version; - maat_rt->table_rt_mgr = maat_table_runtime_manager_create(maat_instance->table_mgr); - maat_rt->max_table_num = maat_table_manager_get_size(maat_instance->table_mgr); + maat_rt->table_rt_mgr = table_runtime_manager_create(maat_instance->table_schema_mgr, + maat_instance->nr_worker_thread, + maat_instance->garbage_bin); + maat_rt->max_table_num = table_schema_manager_get_size(maat_instance->table_schema_mgr); + maat_rt->max_thread_num = maat_instance->nr_worker_thread; + + return maat_rt; } -void update_maat_runtime(struct maat_runtime *maat_rt) +void maat_runtime_commit(struct maat_runtime *maat_rt) { for (size_t i = 0; i < maat_rt->max_table_num; i++) { - struct maat_table_runtime *table_rt = maat_table_runtime_get(maat_rt->table_rt_mgr, i); + struct table_runtime *table_rt = table_runtime_get(maat_rt->table_rt_mgr, i); if (NULL == table_rt) { continue; } - enum maat_table_type table_type = maat_table_runtime_get_type(table_rt); + table_runtime_commit(table_rt, maat_rt->max_thread_num); } + + maat_rt->last_update_time = time(NULL); } -void destroy_maat_runtime(struct maat_runtime *maat_rt) +void maat_runtime_destroy(struct maat_runtime *maat_rt) { + if (NULL == maat_rt) { + return; + } + if (maat_rt->table_rt_mgr != NULL) { + table_runtime_manager_destroy(maat_rt->table_rt_mgr); + maat_rt->table_rt_mgr = NULL; + } + + free(maat_rt); } -void maat_start_cb(uint64_t new_version, int update_type, void *u_param) +int maat_runtime_updating_flag(struct maat_runtime *maat_rt) +{ + for (size_t i = 0; i < maat_rt->max_table_num; i++) { + struct table_runtime *table_rt = table_runtime_get(maat_rt->table_rt_mgr, i); + if (NULL == table_rt) { + continue; + } + + int flag = table_runtime_updating_flag(table_rt); + if (1 == flag) { + return 1; + } + } + + return 0; +} + +void maat_start_cb(long long new_version, int update_type, void *u_param) { struct maat *maat_instance = (struct maat *)u_param; - if (update_type == CONFIG_UPDATE_TYPE_FULL) { - maat_instance->rebuilding_maat_rt = create_maat_runtime(new_version, maat_instance); + if (update_type == CM_UPDATE_TYPE_FULL) { + maat_instance->creating_maat_rt = maat_runtime_create(new_version, maat_instance); } else { maat_instance->maat_version = new_version; } + + table_schema_manager_all_plugin_cb_start(maat_instance->table_schema_mgr, update_type); } -int maat_update_cb(const char* table_name, const char* line, void *u_param) +int maat_update_cb(const char *table_name, const char *line, void *u_param) { struct maat *maat_instance =(struct maat *)u_param; struct maat_runtime* maat_rt = NULL; - int table_id = maat_table_manager_get_table_id(maat_instance->table_mgr, table_name); - struct maat_table_schema* ptable = maat_table_manager_get_table_desc(maat_instance->table_mgr, table_id); + int table_id = table_schema_manager_get_table_id(maat_instance->table_schema_mgr, table_name); + struct table_schema* table_schema = table_schema_get(maat_instance->table_schema_mgr, table_id); - if (maat_instance->rebuilding_maat_rt != NULL) { - maat_rt = maat_instance->rebuilding_maat_rt; + if (maat_instance->creating_maat_rt != NULL) { + maat_rt = maat_instance->creating_maat_rt; } else { maat_rt = maat_instance->maat_rt; } - //TODO: update rule for table_schema - update_maat_runtime(); + struct table_item *table_item = table_schema_line_to_item(line, table_schema); + struct table_runtime *table_rt = table_runtime_get(maat_rt->table_rt_mgr, table_id); + table_runtime_update(table_rt, table_schema, line, table_item); + free(table_item); + return 0; } +uint32_t maat_runtime_rule_num(struct maat_runtime *maat_rt) +{ + uint32_t total = 0; + struct table_runtime *table_rt = NULL; + + for (size_t i = 0; i < maat_rt->max_table_num; i++) { + table_rt = table_runtime_get(maat_rt->table_rt_mgr, i); + if (table_rt != NULL) { + total += table_runtime_rule_count(table_rt); + } + } + + return total; +} + void maat_finish_cb(void *u_param) { struct maat *maat_instance = (struct maat *)u_param; - if (maat_instance->latest_maat_rt != NULL) { - update_maat_runtime(maat_instance->latest_maat_rt); + table_schema_manager_all_plugin_cb_finish(maat_instance->table_schema_mgr); + + if (maat_instance->creating_maat_rt != NULL) { + maat_instance->creating_maat_rt->rule_num = maat_runtime_rule_num(maat_instance->creating_maat_rt); + maat_runtime_commit(maat_instance->creating_maat_rt); + fprintf(stdout, "Full config version %llu load %d entries complete", + maat_instance->creating_maat_rt->version, + maat_instance->creating_maat_rt->rule_num); + } else if (maat_instance->maat_rt != NULL) { + maat_instance->maat_rt->rule_num = maat_runtime_rule_num(maat_instance->maat_rt); + maat_instance->maat_rt->version = maat_instance->maat_version; + maat_runtime_commit(maat_instance->maat_rt); + fprintf(stdout, "Inc config version %llu load %d entries complete", + maat_instance->maat_rt->version, + maat_instance->maat_rt->rule_num); } - - // - } void *rule_monitor_loop(void *arg) { - char err_str[NAME_MAX] = {0}; + /* Defined by prctl: The name can be up to 16 bytes long, and should + be null terminated if it contains fewer bytes. */ + char maat_name[16] = {0}; struct maat *maat_instance = (struct maat *)arg; + if (strlen(maat_instance->instance_name) > 0) { + snprintf(maat_name, sizeof(maat_name), "MAAT_%s", maat_instance->instance_name); + } else { + snprintf(maat_name, sizeof(maat_name), "MAAT"); + } + + int ret = prctl(PR_SET_NAME, (unsigned long long)maat_name, NULL, NULL, NULL); + assert(ret >= 0); + + pthread_mutex_lock(&(maat_instance->background_update_mutex)); + /* if deferred load on */ + if (maat_instance->deferred_load != 0) { + fprintf(stdout, "Deferred Loading ON, updating in %s.", __func__); + maat_read_full_config(maat_instance); + } + pthread_mutex_unlock(&(maat_instance->background_update_mutex)); + while (maat_instance->is_running) { - usleep(1000 * 1000); + usleep(maat_instance->rule_update_checking_interval_ms * 1000); if( 0 == pthread_mutex_trylock(&(maat_instance->background_update_mutex))) { - switch (maat_instance->rule_import_type) { - case RULE_IMPORT_TYPE_IRIS: + switch (maat_instance->input_mode) { + case DATA_SOURCE_IRIS_FILE: config_monitor_traverse(maat_instance->maat_version, maat_instance->iris_ctx.inc_dir, maat_start_cb, @@ -106,25 +193,38 @@ void *rule_monitor_loop(void *arg) break; } - if (maat_instance->latest_maat_rt != NULL) { + if (maat_instance->creating_maat_rt != NULL) { struct maat_runtime *old_maat_rt = maat_instance->maat_rt; - maat_instance->maat_rt = maat_instance->latest_maat_rt; + maat_instance->maat_rt = maat_instance->creating_maat_rt; if (old_maat_rt != NULL) { - + maat_garbage_bagging(maat_instance->garbage_bin, old_maat_rt, (void (*)(void*))maat_runtime_destroy); } - maat_instance->latest_maat_rt = NULL; + maat_instance->creating_maat_rt = NULL; maat_instance->maat_version = maat_instance->maat_rt->version; maat_instance->last_full_version = maat_instance->maat_rt->version; } if (maat_instance->maat_rt != NULL) { - update_maat_runtime(maat_instance->maat_rt); + int updating_flag = maat_runtime_updating_flag(maat_instance->maat_rt); + time_t time_window = time(NULL) - maat_instance->maat_rt->last_update_time; + + if ((updating_flag > 0) && (time_window >= maat_instance->rule_effect_interval_ms / 1000)) { + maat_runtime_commit(maat_instance->maat_rt); + } + } pthread_mutex_unlock(&(maat_instance->background_update_mutex)); } - + maat_garbage_collect_routine(maat_instance->garbage_bin); } + + maat_runtime_destroy(maat_instance->maat_rt); + maat_garbage_bin_free(maat_instance->garbage_bin); + table_schema_manager_destroy(maat_instance->table_schema_mgr); + free(maat_instance); + + return NULL; } \ No newline at end of file diff --git a/src/maat_table_runtime.cpp b/src/maat_table_runtime.cpp index d2c035c..3ebe716 100644 --- a/src/maat_table_runtime.cpp +++ b/src/maat_table_runtime.cpp @@ -8,55 +8,108 @@ *********************************************************************************************** */ +#include #include #include #include #include +#include "utils.h" #include "maat_utils.h" #include "maat_table_runtime.h" #include "uthash/uthash.h" #include "maat_ex_data.h" #include "adapter_hs.h" +#include "rcu_hash.h" +#include "IPMatcher.h" -struct maat_expr_runtime { - struct adapter_hs *hs; - //rcu_hash *htable; +#define MAAT_MAX_EXPR_ITEM_NUM 8 + +struct plugin_ex_data { + void *data; + void *user_data; }; -struct maat_ip_plugin_runtime { - //struct ip_matcher* ip_matcher; +struct plugin_user_ctx { + int table_id; + struct ex_data_schema *ex_schema; +}; + +struct expr_runtime { + enum scan_mode scan_mode; + struct adapter_hs *hs; + struct adapter_hs_stream *hs_stream; + struct rcu_hash_table *htable; +}; + +struct ip_runtime { + struct ip_matcher *ip_matcher; + struct rcu_hash_table *htable; +}; + +struct plugin_runtime { + uint64_t acc_line_num; + struct ex_data_runtime *ex_data_rt; +}; + +struct ip_plugin_runtime { + struct ip_matcher* ip_matcher; struct ex_data_runtime* ex_data_rt; }; -struct maat_table_runtime { - enum maat_table_type table_type; // table schema已有type?? +struct table_runtime { uint32_t rule_num; - + uint32_t updating_rule_num; + enum table_type table_type; union { - struct maat_expr_runtime *expr_rt; - struct maat_ip_plugin_runtime *ip_plugin_rt; + struct expr_runtime expr_rt; + struct ip_runtime ip_rt; + struct plugin_runtime plugin_rt; + struct ip_plugin_runtime ip_plugin_rt; }; + struct maat_garbage_bin *ref_garbage_bin; //ex_data_rt //table相关指针 }; -struct maat_table_runtime_manager { - struct maat_table_runtime **table_rt; +struct table_runtime_manager { + struct table_runtime **table_rt; size_t n_table_rt; + + struct maat_garbage_bin *garbage_bin; }; -struct maat_table_runtime *table_runtime_new(enum maat_table_type table_type, int max_thread_num, struct maat_garbage_bin* bin) +void plugin_ex_data_free(void *user_ctx, void *data) { - struct maat_table_runtime *table_rt = ALLOC(struct maat_table_runtime, 1); - table_rt->table_type = table_type; - switch (table_type) { + struct plugin_user_ctx *ctx = (struct plugin_user_ctx *)user_ctx; + long argl = ctx->ex_schema->argl; + void *argp = ctx->ex_schema->argp; + ctx->ex_schema->free_func(ctx->table_id, &data, argl, argp); +} + +void expr_ex_data_free(void *user_ctx, void *data) +{ + free(data); +} + +struct table_runtime *table_runtime_new(struct table_schema *table_schema, int max_thread_num, struct maat_garbage_bin* bin) +{ + int table_id = table_schema_get_table_id(table_schema); + struct table_runtime *table_rt = ALLOC(struct table_runtime, 1); + table_rt->ref_garbage_bin = bin; + table_rt->table_type = table_schema_get_table_type(table_schema); + + switch (table_rt->table_type) { case TABLE_TYPE_EXPR: - table_rt->expr_rt->ex_data_rt = ex_data_runtime_new(NULL); + table_rt->expr_rt.htable = rcu_hash_new(expr_ex_data_free); + table_rt->expr_rt.scan_mode = expr_table_schema_get_scan_mode(table_schema); + break; + case TABLE_TYPE_PLUGIN: + table_rt->plugin_rt.ex_data_rt = ex_data_runtime_new(table_id, plugin_ex_data_free); break; case TABLE_TYPE_IP_PLUGIN: - table_rt->ip_plugin_rt->ex_data_rt = ex_data_runtime_new(NULL); + table_rt->ip_plugin_rt.ex_data_rt = ex_data_runtime_new(table_id, plugin_ex_data_free); break; default: break; @@ -65,44 +118,51 @@ struct maat_table_runtime *table_runtime_new(enum maat_table_type table_type, in return table_rt; } -void table_runtime_free(struct maat_table_runtime * table_rt) +void table_runtime_free(struct table_runtime * table_rt) { - switch (table_rt->table_type) - { - case TABLE_TYPE_EXPR: - ex_data_runtime_free(table_rt->expr_rt->ex_data_rt); - break; - case TABLE_TYPE_IP_PLUGIN: - ex_data_runtime_free(table_rt->ip_plugin_rt->ex_data_rt); - default: - break; + switch (table_rt->table_type) { + case TABLE_TYPE_EXPR: + adapter_hs_destroy(table_rt->expr_rt.hs); + rcu_hash_free(table_rt->expr_rt.htable); + break; + case TABLE_TYPE_PLUGIN: + ex_data_runtime_free(table_rt->plugin_rt.ex_data_rt); + break; + case TABLE_TYPE_IP_PLUGIN: + ip_matcher_free(table_rt->ip_plugin_rt.ip_matcher); + ex_data_runtime_free(table_rt->ip_plugin_rt.ex_data_rt); + break; + default: + break; } free(table_rt); } -struct maat_table_runtime_manager *maat_table_runtime_manager_create(struct maat_table_manager *table_mgr, int max_thread_num, struct maat_garbage_bin* garbage_bin) +struct table_runtime_manager * +table_runtime_manager_create(struct table_schema_manager *table_schema_mgr, int max_thread_num, + struct maat_garbage_bin* garbage_bin) { - if (NULL == table_mgr) { + if (NULL == table_schema_mgr) { return NULL; } - struct maat_table_runtime_manager *table_rt_mgr = ALLOC(struct maat_table_runtime_manager, 1); - table_rt_mgr->n_table_rt = maat_table_manager_get_size(table_mgr); - table_rt_mgr->table_rt = ALLOC(struct maat_table_runtime *, table_rt_mgr->n_table_rt); + struct table_runtime_manager *table_rt_mgr = ALLOC(struct table_runtime_manager, 1); + table_rt_mgr->n_table_rt = table_schema_manager_get_size(table_schema_mgr); + table_rt_mgr->table_rt = ALLOC(struct table_runtime *, table_rt_mgr->n_table_rt); for (size_t i = 0; i < table_rt_mgr->n_table_rt; i++) { - enum maat_table_type table_type = maat_table_manager_get_table_type(table_mgr, i); - if (table_type == TABLE_TYPE_MAX) { + struct table_schema *table_schema = table_schema_get(table_schema_mgr, i); + if (NULL == table_schema) { continue; } - table_rt_mgr->table_rt[i] = table_runtime_new(table_type, max_thread_num, garbage_bin); + table_rt_mgr->table_rt[i] = table_runtime_new(table_schema, max_thread_num, garbage_bin); } return table_rt_mgr; } -void maat_table_runtime_manager_destroy(struct maat_table_runtime_manager *table_rt_mgr) +void table_runtime_manager_destroy(struct table_runtime_manager *table_rt_mgr) { for(size_t i = 0; i < table_rt_mgr->n_table_rt; i++) { table_runtime_free(table_rt_mgr->table_rt[i]); @@ -114,13 +174,462 @@ void maat_table_runtime_manager_destroy(struct maat_table_runtime_manager *table free(table_rt_mgr); } -struct maat_table_runtime *maat_table_runtime_get(struct maat_table_runtime_manager *table_rt_mgr, int table_id) +struct table_runtime *table_runtime_get(struct table_runtime_manager *table_rt_mgr, int table_id) { assert(table_id < (int)table_rt_mgr->n_table_rt); return table_rt_mgr->table_rt[table_id]; } -enum maat_table_type maat_table_runtime_get_type(struct maat_table_runtime* table_rt) +size_t table_runtime_rule_count(struct table_runtime *table_rt) +{ + return table_rt->rule_num; +} + +enum table_type table_runtime_get_type(struct table_runtime* table_rt) { return table_rt->table_type; } + +int table_runtime_scan_string(struct table_runtime* table_rt, int thread_id, const char *data, size_t data_len, + int result[], size_t *n_result) +{ + return adapter_hs_scan(table_rt->expr_rt.hs, thread_id, data, data_len, result, n_result); +} + +void table_runtime_stream_open(struct table_runtime *table_rt, int thread_id) +{ + struct adapter_hs_stream *hs_stream = adapter_hs_stream_open(table_rt->expr_rt.hs, thread_id); + table_rt->expr_rt.hs_stream = hs_stream; +} + +int table_runtime_scan_stream(struct table_runtime *table_rt, const char *data, size_t data_len, + int result[], size_t *n_result) +{ + return adapter_hs_scan_stream(table_rt->expr_rt.hs_stream, data, data_len, result, n_result); +} + +void table_runtime_stream_close(struct table_runtime *table_rt) +{ + adapter_hs_stream_close(table_rt->expr_rt.hs_stream); + table_rt->expr_rt.hs_stream = NULL; +} + +struct ip_rule *ip_plugin_item_to_ip_rule(struct ip_plugin_item *ip_plugin_item) +{ + return NULL; +} + +and_expr_t *expr_item_to_expr_rule(struct expr_item *expr_item) +{ + size_t i = 0; + size_t sub_expr_cnt = 0; + char *pos = NULL; + char *saveptr = NULL; + char *sub_key_array[MAAT_MAX_EXPR_ITEM_NUM]; + and_expr_t *expr_rule = ALLOC(and_expr_t, 1); + + switch (expr_item->expr_type) { + case EXPR_TYPE_AND: + case EXPR_TYPE_REGEX: + for (i = 0, pos = expr_item->keywords; ; i++, pos = NULL) { + char *tmp = strtok_r_esc(pos, '&', &saveptr); + if (NULL == tmp) { + break; + } + + if (i >= MAAT_MAX_EXPR_ITEM_NUM) { + fprintf(stderr, "item_id:%d too many patterns", expr_item->item_id); + return NULL; + } + + sub_key_array[i] = tmp; + if (expr_item->expr_type == EXPR_TYPE_REGEX) { + sub_key_array[i] = str_unescape_and(sub_key_array[i]); + } else { + sub_key_array[i] = str_unescape(sub_key_array[i]); + } + } + sub_expr_cnt = i; + break; + case EXPR_TYPE_STRING: + sub_expr_cnt = 1; + sub_key_array[0] = expr_item->keywords; + sub_key_array[0] = str_unescape(sub_key_array[0]); + break; + default: + break; + } + + for (i = 0; i < sub_expr_cnt; i++) { + expr_rule->expr_id = expr_item->item_id; + expr_rule->patterns[i].pat = ALLOC(char, strlen(sub_key_array[i])); + memcpy(expr_rule->patterns[i].pat, sub_key_array[i], strlen(sub_key_array[i])); + expr_rule->patterns[i].pat_len = strlen(sub_key_array[i]); + expr_rule->patterns[i].type = expr_item->expr_type; + } + expr_rule->n_patterns = sub_expr_cnt; + + return expr_rule; +} + +void expr_runtime_update_row(struct expr_runtime *expr_rt, char *key, size_t key_len, + and_expr_t *expr_rule, int is_valid) +{ + void *data = NULL; + + if (is_valid == 0) { + //delete + data = rcu_hash_find(expr_rt->htable, key, key_len); + if (NULL == data) { + //log_error("the key:%s not exist, so can't be deleted."); + return; + } + rcu_hash_del(expr_rt->htable, key, key_len); + } else { + //add + data = rcu_hash_find(expr_rt->htable, key, key_len); + if (data != NULL) { + //log_error("the key:%s already exist, so can't be added."); + return; + } + and_expr_t *data = ALLOC(and_expr_t, 1); + memcpy(data, expr_rule, sizeof(and_expr_t)); + rcu_hash_add(expr_rt->htable, key, key_len, (void *)data); + } +} + +void plugin_runtime_update_row(struct plugin_runtime *plugin_rt, struct table_schema *table_schema, + const char *row, char *key, size_t key_len, int is_valid) +{ + struct plugin_ex_data *ex_data = ALLOC(struct plugin_ex_data, 1); + ex_data->data = ex_data_runtime_row2ex_data(plugin_rt->ex_data_rt, row, key, key_len); + int set_flag = plugin_table_schema_ex_data_schema_flag(table_schema); + size_t cb_count = plugin_table_schema_callback_count(table_schema); + + /* already set plugin_table_schema's ex_data_schema */ + if (1 == set_flag) { + if (is_valid == 0) { + // delete + ex_data_runtime_del_ex_data(plugin_rt->ex_data_rt, key, key_len); + } else { + // add + ex_data_runtime_add_ex_data(plugin_rt->ex_data_rt, key, key_len, (void *)ex_data); + } + } + + /* plugin table schema has callback */ + if (cb_count > 0) { + plugin_table_schema_all_cb_update(table_schema, row); + } + + if ((0 == set_flag) && (0 == cb_count)) { + ex_data_runtime_cache_row_put(plugin_rt->ex_data_rt, row); + } + + plugin_rt->acc_line_num++; +} + +void ip_plugin_runtime_update_row(struct ip_plugin_runtime *ip_plugin_rt, struct table_schema *table_schema, + const char *row, char *key, size_t key_len, + struct ip_rule *ip_rule, int is_valid) +{ + struct plugin_ex_data *ex_data = ALLOC(struct plugin_ex_data, 1); + struct ex_data_runtime *ex_data_rt = ip_plugin_rt->ex_data_rt; + ex_data->data = ex_data_runtime_row2ex_data(ex_data_rt, row, key, key_len); + ex_data->user_data = ip_rule; + int set_flag = plugin_table_schema_ex_data_schema_flag(table_schema); + + if (1 == set_flag) { + if (0 == is_valid) { + //delete + ex_data_runtime_del_ex_data(ex_data_rt, key, key_len); + } else { + //add + ex_data_runtime_add_ex_data(ip_plugin_rt->ex_data_rt, key, key_len, ex_data); + } + } else { + ex_data_runtime_cache_row_put(ip_plugin_rt->ex_data_rt, row); + } + +} + +void table_runtime_update(struct table_runtime *table_rt, struct table_schema *table_schema, const char *row, struct table_item *table_item) +{ + int is_valid = -1; + char *key = NULL; + size_t key_len = 0; + and_expr_t *expr_rule = NULL; + struct ip_rule *ip_rule = NULL; + + switch (table_rt->table_type) { + case TABLE_TYPE_EXPR: + is_valid = table_item->expr_item.is_valid; + expr_rule = expr_item_to_expr_rule(&table_item->expr_item); + key = (char *)&(table_item->expr_item.item_id); + expr_runtime_update_row(&(table_rt->expr_rt), key, sizeof(int), expr_rule, is_valid); + free(expr_rule); + break; + case TABLE_TYPE_PLUGIN: + is_valid = table_item->plugin_item.is_valid; + key = table_item->plugin_item.key; + key_len = table_item->plugin_item.key_len; + plugin_runtime_update_row(&(table_rt->plugin_rt), table_schema, row, key, key_len, is_valid); + break; + case TABLE_TYPE_IP_PLUGIN: + is_valid = table_item->ip_plugin_item.is_valid; + ip_rule = ip_plugin_item_to_ip_rule(&table_item->ip_plugin_item); + key = (char *)&(table_item->ip_plugin_item.item_id); + ip_plugin_runtime_update_row(&(table_rt->ip_plugin_rt), table_schema, row, key, sizeof(int), ip_rule, is_valid); + free(ip_rule); + break; + default: + break; + } + + if (is_valid == 0) { + table_rt->rule_num--; + } else { + table_rt->rule_num++; + } +} + +int expr_runtime_commit(struct table_runtime *table_rt, size_t nr_worker_thread) +{ + struct expr_runtime *expr_rt = &(table_rt->expr_rt); + void **ex_data_array = NULL; + and_expr_t *rules = NULL; + size_t rule_cnt = 0; + int ret = 0; + + rule_cnt = rcu_hash_list_updating_data(expr_rt->htable, &ex_data_array); + rules = ALLOC(and_expr_t, rule_cnt); + + for (size_t i = 0; i < rule_cnt; i++) { + rules[i] = *(and_expr_t *)ex_data_array[i]; + } + + struct adapter_hs *new_adapter_hs = NULL; + struct adapter_hs *old_adapter_hs = NULL; + if (rule_cnt > 0) { + new_adapter_hs = adapter_hs_initialize(expr_rt->scan_mode, nr_worker_thread, rules, rule_cnt); + if (NULL == new_adapter_hs) { + ret = -1; + } + } + + old_adapter_hs = expr_rt->hs; + expr_rt->hs = new_adapter_hs; + + maat_garbage_bagging(table_rt->ref_garbage_bin, old_adapter_hs, (void (*)(void*))adapter_hs_destroy); + rcu_hash_commit(expr_rt->htable); + table_rt->rule_num = rcu_hash_count(expr_rt->htable); + + free(rules); + free(ex_data_array); + + return ret; +} + +int plugin_runtime_commit(struct table_runtime *table_rt) +{ + ex_data_runtime_commit(table_rt->plugin_rt.ex_data_rt); + table_rt->rule_num = ex_data_runtime_ex_data_count(table_rt->plugin_rt.ex_data_rt); + + return 0; +} + +int ip_plugin_runtime_commit(struct table_runtime *table_rt) +{ + struct plugin_ex_data **ex_data_array = NULL; + struct ip_rule *rules = NULL; + size_t rule_cnt = 0; + int ret = 0; + struct ip_plugin_runtime *ip_plugin_rt = &(table_rt->ip_plugin_rt); + + rule_cnt = ex_data_runtime_list_updating_ex_data(ip_plugin_rt->ex_data_rt, (void ***)&ex_data_array); + rules = ALLOC(struct ip_rule, rule_cnt); + + for (size_t i = 0; i < rule_cnt; i++) { + rules[i] = *(struct ip_rule *)ex_data_array[i]->user_data; + } + + struct ip_matcher *new_ip_matcher = NULL; + struct ip_matcher *old_ip_matcher = NULL; + size_t mem_used = 0; + + if (rule_cnt > 0) { + new_ip_matcher = ip_matcher_new(rules, rule_cnt, &mem_used); + if (NULL == new_ip_matcher) { + ret = -1; + } + } + + old_ip_matcher = ip_plugin_rt->ip_matcher; + ip_plugin_rt->ip_matcher = new_ip_matcher; + maat_garbage_bagging(table_rt->ref_garbage_bin, old_ip_matcher, (void (*)(void*))ip_matcher_free); + ex_data_runtime_commit(ip_plugin_rt->ex_data_rt); + table_rt->rule_num = ex_data_runtime_ex_data_count(ip_plugin_rt->ex_data_rt); + + free(rules); + free(ex_data_array); + + return ret; +} + +int expr_runtime_updating_flag(struct expr_runtime *expr_rt) +{ + return rcu_hash_updating_flag(expr_rt->htable); +} + +int plugin_runtime_updating_flag(struct plugin_runtime *plugin_rt) +{ + return ex_data_runtime_updating_flag(plugin_rt->ex_data_rt); +} + +int ip_plugin_runtime_updating_flag(struct ip_plugin_runtime *ip_plugin_rt) +{ + return ex_data_runtime_updating_flag(ip_plugin_rt->ex_data_rt); +} + +int table_runtime_updating_flag(struct table_runtime *table_rt) +{ + int updating_flag = 0; + + switch (table_rt->table_type) { + case TABLE_TYPE_EXPR: + updating_flag = expr_runtime_updating_flag(&(table_rt->expr_rt)); + break; + case TABLE_TYPE_PLUGIN: + updating_flag = plugin_runtime_updating_flag(&(table_rt->plugin_rt)); + break; + case TABLE_TYPE_IP_PLUGIN: + updating_flag = ip_plugin_runtime_updating_flag(&(table_rt->ip_plugin_rt)); + break; + default: + break; + } + + return updating_flag; +} + +void table_runtime_commit(struct table_runtime *table_rt, size_t nr_worker_thread) +{ + switch (table_rt->table_type) { + case TABLE_TYPE_EXPR: + expr_runtime_commit(table_rt, nr_worker_thread); + break; + case TABLE_TYPE_PLUGIN: + plugin_runtime_commit(table_rt); + case TABLE_TYPE_IP_PLUGIN: + ip_plugin_runtime_commit(table_rt); + break; + default: + break; + } +} + +size_t plugin_table_runtime_cached_row_count(struct table_runtime* table_rt) +{ + size_t row_count = 0; + struct ex_data_runtime *ex_data_rt = NULL; + + switch (table_rt->table_type) { + case TABLE_TYPE_IP_PLUGIN: + ex_data_rt = table_rt->ip_plugin_rt.ex_data_rt; + break; + default: + break; + } + row_count = ex_data_runtime_cached_row_count(ex_data_rt); + + return row_count; +} + +const char* plugin_table_runtime_get_cached_row(struct table_runtime* table_rt, size_t row_seq) +{ + const char *line = NULL; + struct ex_data_runtime *ex_data_rt = NULL; + + switch (table_rt->table_type) { + case TABLE_TYPE_IP_PLUGIN: + ex_data_rt = table_rt->ip_plugin_rt.ex_data_rt; + break; + default: + break; + } + line = ex_data_runtime_cached_row_get(ex_data_rt, row_seq); + + return line; +} + +void *plugin_table_runtime_get_ex_data(struct table_runtime *table_rt, struct table_schema *table_schema, + const char *key, size_t key_len) +{ + void *ex_data = NULL; + int set_flag = plugin_table_schema_ex_data_schema_flag(table_schema); + if (0 == set_flag) { + assert(0); + return NULL; + } + + enum table_type table_type = table_schema_get_table_type(table_schema); + switch (table_type) { + case TABLE_TYPE_PLUGIN: + ex_data = ex_data_runtime_get_ex_data(table_rt->plugin_rt.ex_data_rt, key, key_len); + break; + case TABLE_TYPE_IP_PLUGIN: + break; + default: + break; + } + + return ex_data; +} + +struct ex_data_runtime *table_runtime_get_ex_data_rt(struct table_runtime *table_rt) +{ + struct ex_data_runtime *ex_data_rt = NULL; + + switch (table_rt->table_type) { + case TABLE_TYPE_PLUGIN: + ex_data_rt = table_rt->plugin_rt.ex_data_rt; + break; + case TABLE_TYPE_IP_PLUGIN: + ex_data_rt = table_rt->ip_plugin_rt.ex_data_rt; + break; + default: + break; + } + + return ex_data_rt; +} + +void plugin_table_runtime_commit_ex_data_schema(struct table_runtime *table_rt, struct table_schema *table_schema) +{ + struct ex_data_schema *ex_data_schema = plugin_table_schema_get_ex_data_schema(table_schema); + struct ex_data_runtime *ex_data_rt = table_runtime_get_ex_data_rt(table_rt); + ex_data_runtime_set_schema(ex_data_rt, ex_data_schema); + struct plugin_user_ctx *user_ctx = ALLOC(struct plugin_user_ctx, 1); + + user_ctx->table_id = table_schema_get_table_id(table_schema); + user_ctx->ex_schema = ex_data_schema; + ex_data_runtime_set_user_ctx(ex_data_rt, user_ctx); + + size_t n_cached_row = ex_data_runtime_cached_row_count(ex_data_rt); + for (size_t i = 0; i < n_cached_row; i++) { + const char *row = ex_data_runtime_cached_row_get(ex_data_rt, i); + switch (table_rt->table_type) { + case TABLE_TYPE_PLUGIN: + plugin_runtime_update_row(&(table_rt->plugin_rt), table_schema, row, NULL, 0, 1); + break; + case TABLE_TYPE_IP_PLUGIN: + ip_plugin_runtime_update_row(&(table_rt->ip_plugin_rt), table_schema, NULL, NULL, 0, NULL, 1); + break; + default: + break; + } + } + ex_data_runtime_clear_row_cache(ex_data_rt); + + table_runtime_commit(table_rt, 0); +} diff --git a/src/maat_table_schema.cpp b/src/maat_table_schema.cpp index 1bcc53e..bc3ed15 100644 --- a/src/maat_table_schema.cpp +++ b/src/maat_table_schema.cpp @@ -11,53 +11,19 @@ #include #include #include +#include +#include "utils.h" #include "maat_utils.h" #include "maat_table_schema.h" #include "maat_kv.h" -#include "cJSON.h" +#include "cJSON/cJSON.h" #include "adapter_hs.h" #include "maat_ex_data.h" - -#define MAX_TABLE_NUM 256 -#define MAX_TABLE_LINE_SIZE (1024 * 16) -#define MAX_ITEM_STR 64 - -struct expr_item { - int item_id; - int group_id; - char district[MAX_ITEM_STR]; //128 - char keywords[MAX_ITEM_STR]; //1k - char expr_type[MAX_ITEM_STR];//enum - char match_method[MAX_ITEM_STR]; //enum - int is_hexbin; - int case_sensitive; - int is_valid; - - //rule_tag; 只存在schema里 - //int have_exdata; - //struct ex_data *ex_data; //hash表 -}; - -struct ip_plugin_item { - int row_id; - int ip_type; - int start_ip; - int end_ip; - int valid_flag; - int rule_tag; - int have_exdata; - struct ex_data *ex_data; -}; - -struct maat_table_item { - enum maat_table_type table_type; - union { - struct expr_item expr_item; - struct ip_plugin_item ip_plugin_item; - }; -}; +#define MAX_TABLE_NUM 256 +#define MAX_TABLE_LINE_SIZE (1024 * 16) +#define MAX_FOREIGN_CLMN_NUM 8 struct expr_table_schema { int item_id_column; @@ -67,119 +33,147 @@ struct expr_table_schema { int expr_type_column; int match_method_column; int is_hexbin_column; - int case_sensitive_column; - /* valid means add, invalid means delete */ - int is_valid_column; - int have_exdata; - struct ex_data_schema *ex_schema; + int is_valid_column; /* valid means add, invalid means delete */ + enum scan_mode scan_mode; /* adapter_hs scan mode */ +}; + +#define MAX_PLUGIN_PER_TABLE 32 +struct plugin_table_schema { + int item_id_column; + int key_column; + int is_valid_column; + int rule_tag_column; + int n_foreign; + int foreign_columns[MAX_FOREIGN_CLMN_NUM]; + size_t cb_plug_cnt; + struct plugin_table_callback_schema cb_plug[MAX_PLUGIN_PER_TABLE]; + struct ex_data_schema ex_schema; }; struct ip_plugin_table_schema { - int row_id_column; + int item_id_column; int ip_type_column; int start_ip_column; int end_ip_column; - int valid_flag_column; + int is_valid_column; int rule_tag_column; int have_exdata; - struct ex_data_schema *ex_schema; + struct ex_data_schema ex_schema; }; -struct maat_table_schema { +struct table_schema { int table_id; - sds table_name; - enum maat_table_type table_type; + char table_name[NAME_MAX]; + enum table_type table_type; union { struct expr_table_schema expr; + struct plugin_table_schema plugin; struct ip_plugin_table_schema ip_plugin; }; }; -struct maat_table_manager { - struct maat_table_schema *schema_table[MAX_TABLE_NUM]; +struct table_schema_manager { + struct table_schema *schema_table[MAX_TABLE_NUM]; size_t n_schema_table; - + size_t n_active_plugin_table; struct maat_kv_store *tablename2id_map; }; -struct maat_table_schema *table_schema_new(void) +struct table_schema *table_schema_new(void) { - struct maat_table_schema *ptable = ALLOC(struct maat_table_schema, 1); + struct table_schema *ptable = ALLOC(struct table_schema, 1); return ptable; } -void table_schema_free(struct maat_table_schema *ptable) +void table_schema_free(struct table_schema *ptable) { free(ptable); } -int read_expr_table_schema(cJSON *root, struct maat_table_schema *ptable) +int read_expr_table_schema(cJSON *root, struct table_schema *ptable, + struct maat_kv_store* reserved_word_map) { int read_cnt = 0; - cJSON *item = NULL; + cJSON *json = NULL; - item = cJSON_GetObjectItem(root, "table_id"); - if (item != NULL && item->type == cJSON_String) { - ptable->table_id = item->valueint; + json = cJSON_GetObjectItem(root, "table_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->table_id = json->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "table_name"); - if (item != NULL && item->type == cJSON_String) { - memcpy(ptable->table_name, item->valuestring, strlen(item->valuestring)); + json = cJSON_GetObjectItem(root, "table_name"); + if (json != NULL && json->type == cJSON_String) { + if (strlen(json->valuestring) >= NAME_MAX) { + fprintf(stderr, "table name %s length too long", json->valuestring); + return -1; + } + memcpy(ptable->table_name, json->valuestring, strlen(json->valuestring)); read_cnt++; } - item = cJSON_GetObjectItem(root, "item_id"); - if (item != NULL && item->type == cJSON_Number) { - ptable->expr.item_id_column = item->valueint; + json = cJSON_GetObjectItem(root, "scan_mode"); + if (json != NULL && json->type == cJSON_String) { + int ret = maat_kv_read(reserved_word_map, json->valuestring, (int*)&(ptable->expr.scan_mode)); + if (ret < 0) { + fprintf(stderr, "scan_mode %s illegal", json->valuestring); + return -1; + } read_cnt++; } - item = cJSON_GetObjectItem(root, "group_id"); - if (item != NULL && item->type == cJSON_Number) { - ptable->expr.group_id_column = item->valueint; + json = cJSON_GetObjectItem(root, "item_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->expr.item_id_column = json->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "district"); - if (item != NULL && item->type == cJSON_Number) { - ptable->expr.district_column = item->valueint; + json = cJSON_GetObjectItem(root, "group_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->expr.group_id_column = json->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "keywords"); + json = cJSON_GetObjectItem(root, "rule"); + if (json == NULL || json->type != cJSON_Object) { + fprintf(stderr, "table %s has no rule", ptable->table_name); + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, "keywords"); if (item != NULL && item->type == cJSON_Number) { ptable->expr.keywords_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "expr_type"); + if (ptable->table_type == TABLE_TYPE_EXPR_PLUS) { + item = cJSON_GetObjectItem(json, "district"); + if (item != NULL && item->type == cJSON_Number) { + ptable->expr.district_column = item->valueint; + read_cnt++; + } + } + + item = cJSON_GetObjectItem(json, "expr_type"); if (item != NULL && item->type == cJSON_Number) { ptable->expr.expr_type_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "match_method"); + item = cJSON_GetObjectItem(json, "match_method"); if (item != NULL && item->type == cJSON_Number) { ptable->expr.match_method_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "is_hexbin"); + item = cJSON_GetObjectItem(json, "is_hexbin"); if (item != NULL && item->type == cJSON_Number) { ptable->expr.is_hexbin_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "case_sensitive"); - if (item != NULL && item->type == cJSON_Number) { - ptable->expr.case_sensitive_column = item->valueint; - read_cnt++; - } - - item = cJSON_GetObjectItem(root, "is_valid"); + item = cJSON_GetObjectItem(json, "is_valid"); if (item != NULL && item->type == cJSON_Number) { ptable->expr.is_valid_column = item->valueint; read_cnt++; @@ -192,50 +186,152 @@ int read_expr_table_schema(cJSON *root, struct maat_table_schema *ptable) } } -int read_ip_plugin_table_schema(cJSON *root, struct maat_table_schema *ptable) +static int read_integer_array(char *string, int *array, int size) { - int read_cnt = 0; - cJSON *item = NULL; + int i = 0; + char *token = NULL, *sub_token = NULL, *saveptr; + + for (i = 0, token = string; i < size; token= NULL, i++) { + sub_token = strtok_r(token, ",", &saveptr); + if (sub_token == NULL) { + break; + } + + sscanf(sub_token, "%d", array + i); + } - item = cJSON_GetObjectItem(root, "table_id"); + return i; +} + +int read_plugin_table_schema(cJSON *root, struct table_schema *ptable) +{ + size_t read_cnt = 0; + cJSON *json = NULL; + + json = cJSON_GetObjectItem(root, "table_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->table_id = json->valueint; + read_cnt++; + } + + json = cJSON_GetObjectItem(root, "table_name"); + if (json != NULL && json->type == cJSON_String) { + if (strlen(json->valuestring) >= NAME_MAX) { + fprintf(stderr, "table name %s length too long", json->valuestring); + return -1; + } + memcpy(ptable->table_name, json->valuestring, strlen(json->valuestring)); + read_cnt++; + } + + json = cJSON_GetObjectItem(root, "item_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->plugin.item_id_column = json->valueint; + read_cnt++; + } + + json = cJSON_GetObjectItem(root, "rule"); + if (json == NULL || json->type != cJSON_Object) { + fprintf(stderr, "table %s has no rule", ptable->table_name); + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, "key"); if (item != NULL && item->type == cJSON_Number) { - ptable->table_id = item->valueint; + ptable->plugin.key_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "table_name"); - if (item != NULL && item->type == cJSON_String) { - memcpy(ptable->table_name, item->valuestring, strlen(item->valuestring)); - read_cnt++; - } - - item = cJSON_GetObjectItem(root, "row_id"); + item = cJSON_GetObjectItem(json, "is_valid"); if (item != NULL && item->type == cJSON_Number) { - ptable->ip_plugin.row_id_column = item->valueint; + ptable->plugin.is_valid_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "ip_type"); + item = cJSON_GetObjectItem(json, "tag"); + if (item != NULL && item->type == cJSON_Number) { + ptable->plugin.rule_tag_column = item->valueint; + read_cnt++; + } + + item = cJSON_GetObjectItem(json, "foreign"); + if (item != NULL) { + read_cnt++; + if (item->type == cJSON_String) { + ptable->plugin.n_foreign = read_integer_array(item->valuestring, + ptable->plugin.foreign_columns, + MAX_FOREIGN_CLMN_NUM); + } else if (item->type == cJSON_Array) { + ptable->plugin.n_foreign = cJSON_GetArraySize(item); + for (int i = 0; i < ptable->plugin.n_foreign; i++) { + cJSON *foreign_item = cJSON_GetArrayItem(item, i); + assert(foreign_item->type == cJSON_Number); + ptable->plugin.foreign_columns[i] = foreign_item->valueint; + } + } + } + + if (read_cnt < 7) { + return -1; + } else { + return 0; + } +} + +int read_ip_plugin_table_schema(cJSON *root, struct table_schema *ptable) +{ + size_t read_cnt = 0; + cJSON *json = NULL; + + json = cJSON_GetObjectItem(root, "table_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->table_id = json->valueint; + read_cnt++; + } + + json = cJSON_GetObjectItem(root, "table_name"); + if (json != NULL && json->type == cJSON_String) { + if (strlen(json->valuestring) >= NAME_MAX) { + fprintf(stderr, "table name %s length too long", json->valuestring); + return -1; + } + memcpy(ptable->table_name, json->valuestring, strlen(json->valuestring)); + read_cnt++; + } + + json = cJSON_GetObjectItem(root, "item_id"); + if (json != NULL && json->type == cJSON_Number) { + ptable->ip_plugin.item_id_column = json->valueint; + read_cnt++; + } + + json = cJSON_GetObjectItem(root, "rule"); + if (NULL == json || json->type != cJSON_Object) { + fprintf(stderr, "table %s has no rule", ptable->table_name); + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, "ip_type"); if (item != NULL && item->type == cJSON_Number) { ptable->ip_plugin.ip_type_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "start_ip"); + item = cJSON_GetObjectItem(json, "start_ip"); if (item != NULL && item->type == cJSON_Number) { ptable->ip_plugin.start_ip_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "end_ip"); + item = cJSON_GetObjectItem(json, "end_ip"); if (item != NULL && item->type == cJSON_Number) { ptable->ip_plugin.end_ip_column = item->valueint; read_cnt++; } - item = cJSON_GetObjectItem(root, "valid"); + item = cJSON_GetObjectItem(json, "is_valid"); if (item != NULL && item->type == cJSON_Number) { - ptable->ip_plugin.valid_flag_column = item->valueint; + ptable->ip_plugin.is_valid_column = item->valueint; read_cnt++; } @@ -246,137 +342,153 @@ int read_ip_plugin_table_schema(cJSON *root, struct maat_table_schema *ptable) } } -int table_schema_populate(const char *line, struct maat_table_schema *ptable, +int table_schema_populate(cJSON *json, struct table_schema *ptable, struct maat_kv_store* reserved_word_map) { int ret = -1; - int read_cnt = 0; - cJSON *root = NULL; cJSON *item = NULL; - sds copy_line = sdsnew(line); - sds table_type_str = NULL; - root = cJSON_Parse(copy_line); - if (NULL == root) { - goto next; - } - - item = cJSON_GetObjectItem(root, "table_type"); + item = cJSON_GetObjectItem(json, "table_type"); if (item != NULL && item->type == cJSON_String) { - table_type_str = sdsnew(item->valuestring); - read_cnt++; + ret = maat_kv_read(reserved_word_map, item->valuestring, (int*)&(ptable->table_type)); + if (ret < 0) { + fprintf(stderr, "table_type %s is illegal", item->valuestring); + return -1; + } } - - ret = maat_kv_read(reserved_word_map, table_type_str, (int*)&(ptable->table_type)); - if (ret < 0) { - goto next; - } - + switch (ptable->table_type) { case TABLE_TYPE_EXPR: - ret = read_expr_table_schema(root, ptable); + case TABLE_TYPE_EXPR_PLUS: + ret = read_expr_table_schema(json, ptable, reserved_word_map); if (ret < 0) { - goto next; + return -1; + } + break; + case TABLE_TYPE_PLUGIN: + ret = read_plugin_table_schema(json, ptable); + if (ret < 0) { + return -1; } break; case TABLE_TYPE_IP_PLUGIN: - ret = read_ip_plugin_table_schema(root, ptable); + ret = read_ip_plugin_table_schema(json, ptable); if (ret < 0) { - goto next; + return -1; } break; default: break; } - -next: - cJSON_Delete(root); - sdsfree(copy_line); - if (table_type_str != NULL) { - sdsfree(table_type_str); - } - return ret; + return 0; } -struct maat_table_manager *maat_table_manager_create(sds table_info_path) +struct table_schema_manager *table_schema_manager_create(const char *table_info_path) { - FILE *fp = fopen(table_info_path, "r"); - if (NULL == fp) { - fprintf(stderr,"Maat read table info %s error.\n",table_info_path); - //log_error() + unsigned char *json_buff = NULL; + size_t json_buff_sz = 0; + + int ret = load_file_to_memory(table_info_path, &json_buff, &json_buff_sz); + if (ret < 0) { + fprintf(stderr, "Maat read table info %s error.\n", table_info_path); return NULL; } - struct maat_kv_store* reserved_word_map = maat_kv_store_new(); + cJSON *root = NULL; + root = cJSON_Parse((const char *)json_buff); + if (!root) { + fprintf(stderr, "Error before: %-200.200s", cJSON_GetErrorPtr()); + free(json_buff); + return NULL; + } + + int json_array_size = cJSON_GetArraySize(root); + if (json_array_size <= 0) { + fprintf(stderr, "invalid json content in %s", table_info_path); + free(json_buff); + return NULL; + } + + struct maat_kv_store* reserved_word_map = maat_kv_store_new(); /* register table type reserved word */ maat_kv_register(reserved_word_map, "expr", TABLE_TYPE_EXPR); + maat_kv_register(reserved_word_map, "expr_plus", TABLE_TYPE_EXPR_PLUS); + maat_kv_register(reserved_word_map, "ip", TABLE_TYPE_IP); + maat_kv_register(reserved_word_map, "plugin", TABLE_TYPE_PLUGIN); maat_kv_register(reserved_word_map, "ip_plugin", TABLE_TYPE_IP_PLUGIN); - int i = 0; - char line[MAX_TABLE_LINE_SIZE] = {0}; - struct maat_table_manager *table_mgr = ALLOC(struct maat_table_manager, 1); - struct maat_table_schema **pptable = table_mgr->schema_table; - table_mgr->tablename2id_map = maat_kv_store_new(); + maat_kv_register(reserved_word_map, "block", SCAN_MODE_BLOCK); + maat_kv_register(reserved_word_map, "stream", SCAN_MODE_STREAM); - while (NULL != fgets(line, sizeof(line), fp)) { - i++; + struct table_schema_manager *table_schema_mgr = ALLOC(struct table_schema_manager, 1); + struct table_schema **pptable = table_schema_mgr->schema_table; + table_schema_mgr->tablename2id_map = maat_kv_store_new(); - if (line[0] == '#' || line[0] == ' ' || line[0] == '\t') { + for (int i = 0; i < json_array_size; i++) { + cJSON *json = cJSON_GetArrayItem(root, i); + + if (json != NULL && json->type == cJSON_Object) { + struct table_schema *table_schema = table_schema_new(); + int ret = table_schema_populate(json, table_schema, reserved_word_map); + if (ret < 0) { + fprintf(stderr, "Maat populate table info error, table_id:%d", table_schema->table_id); + goto error; + } + + if (table_schema->table_id >= MAX_TABLE_NUM) { + fprintf(stderr, "Maat read table info %s:%d error: table id %u > %d.\n", + table_info_path, i, table_schema->table_id, MAX_TABLE_NUM); + goto error; + } + + ret = maat_kv_register(table_schema_mgr->tablename2id_map, table_schema->table_name, table_schema->table_id); + if (ret < 0) { + fprintf(stderr, "Duplicate table %s of table id %d", table_schema->table_name, table_schema->table_id); + goto error; + } + + /* TODO: conjunction table*/ + #if 0 + if (pptable[table_schema->table_id] != NULL) { + fprintf(stderr, "Duplicate table %s of table id %d", table_schema->table_name, table_schema->table_id); + goto error; + } + #endif + + pptable[table_schema->table_id] = table_schema; + table_schema_mgr->n_schema_table++; continue; + error: + table_schema_free(table_schema); } - - struct maat_table_schema *table_schema = table_schema_new(); - int ret = table_schema_populate(line, table_schema, reserved_word_map); - if (ret < 0) { - fprintf(stderr, "Maat read table info %s line %d error: illegal table schema.\n", - table_info_path, i); - //log_error() - goto error; - } - - if (table_schema->table_id >= MAX_TABLE_NUM) { - fprintf(stderr,"Maat read table info %s:%d error: table id %uh > %zu.\n", - table_info_path, i, table_schema->table_id, MAX_TABLE_NUM); - goto error; - } - - ret = maat_kv_register(table_mgr->tablename2id_map, table_schema->table_name, table_schema->table_id); - if (ret < 0) { - //log_error("Duplicate table %s of table id %d", table_schema->table_name, table_schema->table_id); - goto error; - } - - pptable[table_schema->table_id] = table_schema; - table_mgr->n_schema_table++; - continue; - error: - table_schema_free(table_schema); } + maat_kv_store_free(reserved_word_map); + free(json_buff); - return table_mgr; + return table_schema_mgr; } -void maat_table_manager_destroy(struct maat_table_manager *table_mgr) +void table_schema_manager_destroy(struct table_schema_manager *table_schema_mgr) { for (size_t i = 0; i < MAX_TABLE_NUM; i++) { - if (NULL == table_mgr->schema_table[i]) { + if (NULL == table_schema_mgr->schema_table[i]) { continue; } - table_schema_free(table_mgr->schema_table[i]); - table_mgr->schema_table[i] = NULL; + table_schema_free(table_schema_mgr->schema_table[i]); + table_schema_mgr->schema_table[i] = NULL; } - maat_kv_store_free(table_mgr->tablename2id_map); - free(table_mgr); + maat_kv_store_free(table_schema_mgr->tablename2id_map); + free(table_schema_mgr); } -int maat_table_manager_get_table_id(struct maat_table_manager* table_mgr, sds table_name) +int table_schema_manager_get_table_id(struct table_schema_manager* table_schema_mgr, const char *table_name) { int table_id = -1; - int ret=maat_kv_read(table_mgr->tablename2id_map, table_name, &table_id); + int ret = maat_kv_read(table_schema_mgr->tablename2id_map, table_name, &table_id); if (ret > 0) { return table_id; } else { @@ -384,21 +496,134 @@ int maat_table_manager_get_table_id(struct maat_table_manager* table_mgr, sds ta } } -enum maat_table_type maat_table_manager_get_table_type(struct maat_table_manager *table_mgr, int id) +enum table_type table_schema_manager_get_table_type(struct table_schema_manager *table_schema_mgr, int id) { - if (table_mgr->schema_table[id] == NULL) { + if (table_schema_mgr->schema_table[id] == NULL) { return TABLE_TYPE_MAX; } - return table_mgr->schema_table[id]->table_type; + return table_schema_mgr->schema_table[id]->table_type; } -size_t maat_table_manager_get_size(struct maat_table_manager* table_mgr) +size_t table_schema_manager_get_size(struct table_schema_manager* table_schema_mgr) { - return table_mgr->n_schema_table; + return MAX_TABLE_NUM; } -int populate_expr_table_item(sds line, struct expr_table_schema *expr_schema, struct expr_item *expr_item) +void table_schema_manager_all_plugin_cb_start(struct table_schema_manager *table_schema_mgr, int update_type) +{ + struct table_schema *ptable = NULL; + struct plugin_table_schema *plugin_schema = NULL; + + for (size_t i = 0; i < MAX_TABLE_NUM; i++) { + ptable = table_schema_mgr->schema_table[i]; + if (NULL == ptable) { + continue; + } + + plugin_schema = &(ptable->plugin); + if (ptable->table_type != TABLE_TYPE_PLUGIN || 0 == plugin_schema->cb_plug_cnt) { + continue; + } + + table_schema_mgr->n_active_plugin_table++; + for (size_t j = 0; j < plugin_schema->cb_plug_cnt; j++) { + if (plugin_schema->cb_plug[j].start != NULL) { + plugin_schema->cb_plug[j].start(update_type, plugin_schema->cb_plug[j].u_para); + } + } + } +} + +void table_schema_manager_all_plugin_cb_finish(struct table_schema_manager* table_schema_mgr) +{ + struct table_schema *ptable = NULL; + struct plugin_table_schema *plugin_schema = NULL; + + for (size_t i = 0; i < MAX_TABLE_NUM; i++) { + ptable = table_schema_mgr->schema_table[i]; + if (NULL == ptable) { + continue; + } + + if (ptable->table_type != TABLE_TYPE_PLUGIN) { + continue; + } + + plugin_schema = &(ptable->plugin); + for (size_t j = 0; j < plugin_schema->cb_plug_cnt; j++) { + if (plugin_schema->cb_plug[j].finish != NULL) { + plugin_schema->cb_plug[j].finish(plugin_schema->cb_plug[j].u_para); + } + } + } + table_schema_mgr->n_active_plugin_table = 0; +} + +enum expr_type int_to_expr_type(int expr_type) +{ + enum expr_type type = EXPR_TYPE_MAX; + + switch (expr_type) { + case 0: + type = EXPR_TYPE_STRING; + break; + case 1: + type = EXPR_TYPE_AND; + break; + case 2: + type = EXPR_TYPE_REGEX; + break; + case 3: + type = EXPR_TYPE_OFFSET; + break; + default: + break; + } + + return type; +} + +enum match_method int_to_match_method_type(int match_method_type) +{ + enum match_method type = MATCH_METHOD_MAX; + + switch (match_method_type) { + case 0: + type = MATCH_METHOD_SUB; + break; + case 1: + type = MATCH_METHOD_RIGHT; + break; + case 2: + type = MATCH_METHOD_LEFT; + break; + case 3: + type = MATCH_METHOD_COMPLETE; + break; + default: + break; + } + + return type; +} + +struct table_schema *table_schema_get(struct table_schema_manager *table_schema_mgr, int table_id) +{ + return table_schema_mgr->schema_table[table_id]; +} + +enum table_type table_schema_get_table_type(struct table_schema *table_schema) +{ + return table_schema->table_type; +} + +int table_schema_get_table_id(struct table_schema *table_schema) +{ + return table_schema->table_id; +} + +int populate_expr_table_item(const char *line, struct expr_table_schema *expr_schema, struct expr_item *expr_item) { size_t column_offset = 0; size_t column_len = 0; @@ -407,7 +632,6 @@ int populate_expr_table_item(sds line, struct expr_table_schema *expr_schema, st if (ret < 0) { return -1; } - expr_item->item_id = atoi(line + column_offset); ret = get_column_pos(line, expr_schema->group_id_column, &column_offset, &column_len); @@ -416,21 +640,152 @@ int populate_expr_table_item(sds line, struct expr_table_schema *expr_schema, st } expr_item->group_id = atoi(line + column_offset); -} + /* TODO: luis */ + #if 0 + ret = get_column_pos(line, expr_schema->district_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } -int populate_ip_plugin_table_item(sds line, struct ip_plugin_table_schema *ip_plugin_schema, struct ip_plugin_item *ip_plugin_item) -{ + if (column_len >= MAX_DISTRICT_STR) { + //log_error + return -1; + } + memcpy(expr_item->district, (line + column_offset), column_len); + #endif + + ret = get_column_pos(line, expr_schema->keywords_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + + if (column_len >= MAX_KEYWORDS_STR) { + //log_error + return -1; + } + memcpy(expr_item->keywords, (line + column_offset), column_len); + + ret = get_column_pos(line, expr_schema->expr_type_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + + int expr_type = atoi(line + column_offset); + expr_item->expr_type = int_to_expr_type(expr_type); + + ret = get_column_pos(line, expr_schema->match_method_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + + int match_method_type = atoi(line + column_offset); + expr_item->match_method = int_to_match_method_type(match_method_type); + + ret = get_column_pos(line, expr_schema->is_hexbin_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + int db_hexbin = atoi(line + column_offset); + switch (db_hexbin) { + case 0: + expr_item->is_hexbin = FALSE; + expr_item->is_case_sensitive = FALSE; + break; + case 1: + expr_item->is_hexbin = TRUE; + expr_item->is_case_sensitive = FALSE; + break; + case 2: + expr_item->is_hexbin = FALSE; + expr_item->is_case_sensitive = TRUE; + break; + default: + //fprintf(stderr, "invalid hexbin value of expr table"); + return -1; + } + ret = get_column_pos(line, expr_schema->is_valid_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + expr_item->is_valid = atoi(line + column_offset); + return 0; } -struct maat_table_item * -maat_table_line_to_item(sds line, struct maat_table_schema *table_schema) +int populate_plugin_table_item(const char *line, struct plugin_table_schema *plugin_schema, struct plugin_item *plugin_item) +{ + size_t column_offset = 0; + size_t column_len = 0; + + int ret = get_column_pos(line, plugin_schema->key_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + + if (column_len > MAX_KEYWORDS_STR) { + fprintf(stderr, "plugin table's key len:%zu, exceed %d", column_len, MAX_KEYWORDS_STR); + return -1; + } + memcpy(plugin_item->key, (line + column_offset), column_len); + plugin_item->key_len = column_len; + + ret = get_column_pos(line, plugin_schema->is_valid_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + plugin_item->is_valid = atoi(line + column_offset); + + return 0; +} + +int populate_ip_plugin_table_item(const char *line, struct ip_plugin_table_schema *ip_plugin_schema, struct ip_plugin_item *ip_plugin_item) +{ + size_t column_offset = 0; + size_t column_len = 0; + + int ret = get_column_pos(line, ip_plugin_schema->item_id_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + ip_plugin_item->item_id = atoi(line + column_offset); + + ret = get_column_pos(line, ip_plugin_schema->is_valid_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + ip_plugin_item->is_valid = atoi(line + column_offset); + + ret = get_column_pos(line, ip_plugin_schema->ip_type_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + ip_plugin_item->ip_type = atoi(line + column_offset); + if (ip_plugin_item->ip_type != 4 && ip_plugin_item->ip_type != 6) { + return -1; + } + + ret = get_column_pos(line, ip_plugin_schema->start_ip_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + strncpy(ip_plugin_item->start_ip, line + column_offset, MIN(column_len, sizeof(ip_plugin_item->start_ip))); + + ret = get_column_pos(line, ip_plugin_schema->end_ip_column, &column_offset, &column_len); + if (ret < 0) { + return -1; + } + strncpy(ip_plugin_item->end_ip, line + column_offset, MIN(column_len, sizeof(ip_plugin_item->end_ip))); + + return 0; +} + +struct table_item * +table_schema_line_to_item(const char *line, struct table_schema *table_schema) { int ret = -1; - struct maat_table_item *table_item = ALLOC(struct maat_table_item, 1); + struct table_item *table_item = ALLOC(struct table_item, 1); - switch (table_schema->table_type) - { + switch (table_schema->table_type) { case TABLE_TYPE_EXPR: table_item->table_type = TABLE_TYPE_EXPR; ret = populate_expr_table_item(line, &table_schema->expr, &table_item->expr_item); @@ -438,6 +793,13 @@ maat_table_line_to_item(sds line, struct maat_table_schema *table_schema) goto error; } break; + case TABLE_TYPE_PLUGIN: + table_item->table_type = TABLE_TYPE_PLUGIN; + ret = populate_plugin_table_item(line, &table_schema->plugin, &table_item->plugin_item); + if (ret < 0) { + goto error; + } + break; case TABLE_TYPE_IP_PLUGIN: table_item->table_type = TABLE_TYPE_IP_PLUGIN; ret = populate_ip_plugin_table_item(line, &table_schema->ip_plugin, &table_item->ip_plugin_item); @@ -454,3 +816,155 @@ error: return NULL; } +enum scan_mode expr_table_schema_get_scan_mode(struct table_schema *table_schema) +{ + if (table_schema->table_type != TABLE_TYPE_EXPR) { + return SCAN_MODE_MAX; + } + + return table_schema->expr.scan_mode; +} + +int plugin_table_schema_set_ex_data_schema(struct table_schema *table_schema, + maat_plugin_ex_new_func_t *new_func, + maat_plugin_ex_free_func_t *free_func, + maat_plugin_ex_dup_func_t *dup_func, + long argl, void *argp) +{ + if (NULL == new_func || NULL == free_func || NULL == dup_func) { + assert(0); + fprintf(stderr, "%s failed: invalid parameter", __FUNCTION__); + return -1; + } + + struct ex_data_schema *ex_schema = plugin_table_schema_get_ex_data_schema(table_schema); + if (NULL == ex_schema) { + fprintf(stderr, "Error: %s, target table is not a valid plugin table.", __FUNCTION__); + return -1; + } + + if (ex_schema->set_flag) { + assert(0); + fprintf(stderr, "Error: %s, EX data schema already registed.", __FUNCTION__); + return -1; + } + + ex_schema->new_func = new_func; + ex_schema->free_func = free_func; + ex_schema->dup_func = dup_func; + ex_schema->argl = argl; + ex_schema->argp = argp; + ex_schema->set_flag = 1; + + return 0; +} + +struct ex_data_schema *plugin_table_schema_get_ex_data_schema(struct table_schema *table_schema) +{ + struct ex_data_schema *ex_schema = NULL; + + switch (table_schema->table_type) { + case TABLE_TYPE_PLUGIN: + ex_schema = &(table_schema->plugin.ex_schema); + break; + case TABLE_TYPE_IP_PLUGIN: + ex_schema = &(table_schema->ip_plugin.ex_schema); + break; + default: + break; + } + + return ex_schema; +} + +int plugin_table_schema_ex_data_schema_flag(struct table_schema *table_schema) +{ + struct ex_data_schema *ex_schema = NULL; + + switch (table_schema->table_type) { + case TABLE_TYPE_PLUGIN: + ex_schema = &(table_schema->plugin.ex_schema); + break; + case TABLE_TYPE_IP_PLUGIN: + ex_schema = &(table_schema->ip_plugin.ex_schema); + break; + default: + break; + } + + return ex_schema->set_flag; +} + +int plugin_table_schema_add_callback(struct table_schema_manager* table_schema_mgr, int table_id, + maat_start_callback_t *start, + maat_update_callback_t *update, + maat_finish_callback_t *finish, + void *u_para) +{ + struct table_schema *ptable = table_schema_get(table_schema_mgr, table_id); + if (NULL == ptable) { + fprintf(stderr, "table_id:%d unregistered, can't add callback func", table_id); + return -1; + } + + switch (ptable->table_type) { + case TABLE_TYPE_EXPR: + case TABLE_TYPE_EXPR_PLUS: + case TABLE_TYPE_IP: + fprintf(stderr, "table %s is not plugin type.", ptable->table_name); + return -1; + default: + break; + } + + struct plugin_table_schema *plugin_schema = &(ptable->plugin); + size_t idx = plugin_schema->cb_plug_cnt; + if (idx == MAX_PLUGIN_PER_TABLE) { + fprintf(stderr, "the plugin number of table %s exceed maxium:%d", + ptable->table_name, MAX_PLUGIN_PER_TABLE); + return -1; + } + + plugin_schema->cb_plug_cnt++; + plugin_schema->cb_plug[idx].start = start; + plugin_schema->cb_plug[idx].update = update; + plugin_schema->cb_plug[idx].finish = finish; + plugin_schema->cb_plug[idx].u_para = u_para; + + return 0; +} + +size_t plugin_table_schema_callback_count(struct table_schema *table_schema) +{ + if (NULL == table_schema) { + return 0; + } + + if (table_schema->table_type == TABLE_TYPE_PLUGIN) { + return table_schema->plugin.cb_plug_cnt; + } else { + return 0; + } +} + +void plugin_table_schema_all_cb_update(struct table_schema* table_schema, const char *row) +{ + struct plugin_table_schema *plugin_schema = NULL; + + switch (table_schema->table_type) { + case TABLE_TYPE_EXPR: + case TABLE_TYPE_EXPR_PLUS: + case TABLE_TYPE_IP_PLUGIN: + break; + case TABLE_TYPE_PLUGIN: + plugin_schema = &(table_schema->plugin); + if (plugin_schema->cb_plug_cnt > 0) { + for (size_t i = 0; i < plugin_schema->cb_plug_cnt; i++) { + plugin_schema->cb_plug[i].update(table_schema->table_id, row, plugin_schema->cb_plug[i].u_para); + } + } + break; + default: + break; + } +} \ No newline at end of file diff --git a/src/maat_utils.cpp b/src/maat_utils.cpp index 5ebc051..016418e 100644 --- a/src/maat_utils.cpp +++ b/src/maat_utils.cpp @@ -8,28 +8,174 @@ *********************************************************************************************** */ +#include +#include +#include + #include "maat_utils.h" -int get_column_pos(sds line, int column_seq, size_t *offset, size_t *len) +char *maat_strdup(const char *s) { - const char* seps=" \t"; - char* saveptr=NULL, *subtoken=NULL, *str=NULL; - sds dup_line = sdsdup(line); - int i=0, ret=-1; - for (str = dup_line; ; str = NULL) - { + if (NULL == s) { + return NULL; + } + char *d = (char *)malloc(strlen(s) + 1); + memcpy(d, s, strlen(s) + 1); + + return d; +} + +int get_column_pos(const char *line, int column_seq, size_t *offset, size_t *len) +{ + const char *seps=" \t"; + char *saveptr=NULL, *subtoken=NULL, *str=NULL; + char *dup_line = maat_strdup(line); + int i = 0, ret = -1; + for (str = dup_line; ; str = NULL) { subtoken = strtok_r(str, seps, &saveptr); if (subtoken == NULL) break; - if(i==column_seq-1) - { - *offset=subtoken-dup_line; - *len=strlen(subtoken); - ret=0; + if (i == column_seq - 1) { + *offset = subtoken - dup_line; + *len = strlen(subtoken); + ret = 0; break; } i++; } free(dup_line); + return ret; +} + +int load_file_to_memory(const char *file_name, unsigned char **pp_out, size_t *out_sz) +{ + int ret = 0; + FILE *fp = NULL; + struct stat fstat_buf; + size_t read_size=0; + + ret = stat(file_name, &fstat_buf); + if (ret != 0) { + return -1; + } + + fp = fopen(file_name, "r"); + if (fp == NULL) { + return -1; + } + + *out_sz = fstat_buf.st_size; + *pp_out = (unsigned char *)calloc(1, *out_sz+1); + read_size = fread(*pp_out, 1, *out_sz, fp); + if (read_size != *out_sz) { + free(*pp_out); + pp_out = NULL; + return -1; + } + + fclose(fp); + fp = NULL; + return 0; +} + +static char *strchr_esc(char *s, const char delim) +{ + char *token = NULL; + + if (NULL == s) { + return NULL; + } + + for (token = s; *token != '\0'; token++) { + if (*token == '\\') { + token++; + continue; + } + + if(*token == delim) { + break; + } + } + + if (*token == '\0') { + return NULL; + } else { + return token; + } +} + +char *strtok_r_esc(char *s, const char delim, char **save_ptr) +{ + char *token = NULL; + + if (NULL == s) { + s = *save_ptr; + } + + /* Scan leading delimiters. */ + token = strchr_esc(s,delim); + if (NULL == token) { + *save_ptr = token; + return s; + } + + /* Find the end of the token. */ + *token = '\0'; + token++; + *save_ptr = token; + + return s; +} + +char *str_unescape_and(char *s) +{ + size_t i = 0; + size_t j = 0; + + for (i = 0,j = 0; i < strlen(s); i++) { + if (s[i] == '\\' && s[i+1] == '&') { + s[j] = '&'; + i++; + j++; + } else { + s[j] = s[i]; + j++; + } + } + s[j] = '\0'; + return s; +} + +char *str_unescape(char *s) +{ + size_t i=0; + size_t j=0; + + for (i = 0, j = 0; i < strlen(s); i++) { + if (s[i] == '\\') { + switch (s[i+1]) { + case '&': + s[j] = '&'; + break; + case 'b': + s[j] = ' ';//space,0x20; + break; + case '\\': + s[j] = '\\'; + break; + default: + s[j] = s[i]; + i--; //undo the followed i++ + break; + } + i++; + j++; + } else { + s[j] = s[i]; + j++; + } + } + s[j] = '\0'; + return s; } \ No newline at end of file diff --git a/src/rcu_hash.cpp b/src/rcu_hash.cpp index f1c8c6e..dfb330a 100644 --- a/src/rcu_hash.cpp +++ b/src/rcu_hash.cpp @@ -11,12 +11,17 @@ #include #include #include +#include #include "rcu_hash.h" -#include "maat_utils.h" -#include "maat_garbage_collection.h" +#include "utils.h" -#define GARBAGE_DEFAULT_TIMEOUT 60 +struct rcu_hash_garbage_bag { + void *garbage; + void (* garbage_free)(void *garbage); + TAILQ_ENTRY(rcu_hash_garbage_bag) entries; +}; +TAILQ_HEAD(rcu_hash_garbage_q, rcu_hash_garbage_bag); struct rcu_hash_table { int is_updating; @@ -26,8 +31,11 @@ struct rcu_hash_table { struct rcu_hash_node *hashmap_a; struct rcu_hash_node *hashmap_b; - void (* data_free)(void *data); - struct maat_garbage_bin *garbage_bin; + void (*data_free_fn)(void *user_ctx, void *data); + void *user_ctx; + + struct rcu_hash_garbage_q garbage_q; + size_t garbage_q_len; pthread_mutex_t update_mutex; }; @@ -37,35 +45,67 @@ struct rcu_hash_node { size_t key_len; void *data; //table_runtime解析成两个成员 + /* htable the node belongs to */ + struct rcu_hash_table *htable; + UT_hash_handle hh_a; UT_hash_handle hh_b; }; -void rcu_hash_node_free(struct rcu_hash_node *node, void (* data_free)(void *data)) +void rcu_hash_garbage_queue_free(struct rcu_hash_garbage_q* garbage_q) { + struct rcu_hash_garbage_bag *p = NULL; + + while ((p = TAILQ_FIRST(garbage_q)) != NULL) { + p->garbage_free(p->garbage); + TAILQ_REMOVE(garbage_q, p, entries); + free(p); + } +} + +size_t rcu_hash_garbage_queue_len(struct rcu_hash_table *htable) +{ + return htable->garbage_q_len; +} + +void rcu_hash_garbage_bagging(struct rcu_hash_garbage_q* garbage_q, void* garbage, void (* func)(void *)) +{ + struct rcu_hash_garbage_bag *bag = ALLOC(struct rcu_hash_garbage_bag, 1); + + bag->garbage = garbage; + bag->garbage_free = func; + TAILQ_INSERT_TAIL(garbage_q, bag, entries); +} + +void rcu_hash_node_free(struct rcu_hash_node *node) +{ + if (NULL == node) { + return; + } + if (node->key != NULL) { free(node->key); } if (node->data != NULL) { - data_free(node->data); + node->htable->data_free_fn(node->htable->user_ctx, node->data); } - free(node); } -struct rcu_hash_table *rcu_hash_new(void (* data_free)(void *data)) +struct rcu_hash_table *rcu_hash_new(rcu_hash_data_free_fn *free_fn) { - if (NULL == data_free) { + if (NULL == free_fn) { return NULL; } - + struct rcu_hash_table *htable = ALLOC(struct rcu_hash_table, 1); htable->is_updating = 0; htable->effective_hash = 'a'; - htable->garbage_bin = maat_garbage_bin_new(GARBAGE_DEFAULT_TIMEOUT); - htable->data_free = data_free; + TAILQ_INIT(&htable->garbage_q); + htable->garbage_q_len = 0; + htable->data_free_fn = free_fn; pthread_mutex_init(&htable->update_mutex, NULL); return htable; @@ -73,28 +113,36 @@ struct rcu_hash_table *rcu_hash_new(void (* data_free)(void *data)) void rcu_hash_free(struct rcu_hash_table *htable) { + if (NULL == htable) { + return; + } + struct rcu_hash_node *tmp = NULL; struct rcu_hash_node *item = NULL; - - if (htable != NULL) { + if (htable->effective_hash == 'a') { HASH_ITER(hh_a, htable->hashmap_a, item, tmp) { HASH_DELETE(hh_a, htable->hashmap_a, item); - rcu_hash_node_free(item, htable->data_free); + rcu_hash_node_free(item); } - + } else { HASH_ITER(hh_b, htable->hashmap_b, item, tmp) { HASH_DELETE(hh_b, htable->hashmap_b, item); - rcu_hash_node_free(item, htable->data_free); + rcu_hash_node_free(item); } } - maat_garbage_bin_free(htable->garbage_bin); + rcu_hash_garbage_queue_free(&(htable->garbage_q)); pthread_mutex_destroy(&htable->update_mutex); free(htable); } -void rcu_hash_update_prepare(struct rcu_hash_table *htable) +void rcu_hash_set_user_ctx(struct rcu_hash_table *htable, void *user_ctx) +{ + htable->user_ctx = user_ctx; +} + +void rcu_hash_commit_prepare(struct rcu_hash_table *htable) { struct rcu_hash_node *node = NULL; struct rcu_hash_node *tmp = NULL; @@ -116,23 +164,31 @@ void rcu_hash_update_prepare(struct rcu_hash_table *htable) void rcu_hash_add(struct rcu_hash_table *htable, const char *key, size_t key_len, void *data) { + if (NULL == htable || NULL == key || 0 == key_len) { + return; + } + + struct rcu_hash_node *tmp = NULL; struct rcu_hash_node *node = ALLOC(struct rcu_hash_node, 1); + + node->key = (char *)malloc(sizeof(char) * key_len); memcpy(node->key, key, key_len); node->key_len = key_len; node->data = data; + node->htable = htable; if (!htable->is_updating) { - rcu_hash_update_prepare(htable); + rcu_hash_commit_prepare(htable); } if (htable->effective_hash == 'a') { - HASH_FIND(hh_b, htable->hashmap_b, key, key_len, node); - if (NULL == node) { + HASH_FIND(hh_b, htable->hashmap_b, key, key_len, tmp); + if (NULL == tmp) { HASH_ADD_KEYPTR(hh_b, htable->hashmap_b, key, key_len, node); } } else { - HASH_FIND(hh_a, htable->hashmap_a, key, key_len, node); - if (NULL == node) { + HASH_FIND(hh_a, htable->hashmap_a, key, key_len, tmp); + if (NULL == tmp) { HASH_ADD_KEYPTR(hh_a, htable->hashmap_a, key, key_len, node); } } @@ -140,12 +196,15 @@ void rcu_hash_add(struct rcu_hash_table *htable, const char *key, size_t key_len void rcu_hash_del(struct rcu_hash_table *htable, const char *key, size_t key_len) { - struct rcu_hash_node *node = NULL; + if (NULL == htable || NULL == key || 0 == key_len) { + return; + } if (!htable->is_updating) { - rcu_hash_update_prepare(htable); + rcu_hash_commit_prepare(htable); } + struct rcu_hash_node *node = NULL; if (htable->effective_hash == 'a') { HASH_FIND(hh_b, htable->hashmap_b, key, key_len, node); if (node != NULL) { @@ -159,14 +218,18 @@ void rcu_hash_del(struct rcu_hash_table *htable, const char *key, size_t key_len } if (node != NULL) { - maat_garbage_bagging(htable->garbage_bin, node, (void (*)(void*))rcu_hash_node_free); + rcu_hash_garbage_bagging(&(htable->garbage_q), node, (void (*)(void*))rcu_hash_node_free); + htable->garbage_q_len++; } } void *rcu_hash_find(struct rcu_hash_table *htable, const char *key, size_t key_len) { - struct rcu_hash_node *node = NULL; + if (NULL == htable || NULL == key || 0 == key_len) { + return NULL; + } + struct rcu_hash_node *node = NULL; if (htable->effective_hash == 'a') { HASH_FIND(hh_a, htable->hashmap_a, key, key_len, node); if (node != NULL) { @@ -182,8 +245,12 @@ void *rcu_hash_find(struct rcu_hash_table *htable, const char *key, size_t key_l return NULL; } -size_t rcu_hash_counts(struct rcu_hash_table *htable) +size_t rcu_hash_count(struct rcu_hash_table *htable) { + if (NULL == htable) { + return 0; + } + if (htable->effective_hash == 'a') { return HASH_CNT(hh_a, htable->hashmap_a); } else { @@ -193,6 +260,10 @@ size_t rcu_hash_counts(struct rcu_hash_table *htable) void rcu_hash_commit(struct rcu_hash_table *htable) { + if (NULL == htable) { + return; + } + if (!htable->is_updating) { return; } @@ -221,7 +292,40 @@ void rcu_hash_commit(struct rcu_hash_table *htable) } } htable->is_updating = 0; - //maat_garbage_collect_by_force(htable->garbage_bin); - //rcu_garbage + + rcu_hash_garbage_queue_free(&(htable->garbage_q)); + htable->garbage_q_len = 0; + pthread_mutex_unlock(&htable->update_mutex); +} + +size_t rcu_hash_list_updating_data(struct rcu_hash_table *htable, void ***data_array) +{ + size_t i = 0; + size_t node_cnt = 0; + struct rcu_hash_node *node = NULL, *tmp = NULL; + + assert(htable->is_updating == 1); + if (htable->effective_hash == 'a') { + node_cnt = HASH_CNT(hh_b, htable->hashmap_b); + *data_array = ALLOC(void *, node_cnt); + HASH_ITER(hh_b, htable->hashmap_b, node, tmp) { + (*data_array)[i] = node->data; + i++; + } + } else { + node_cnt = HASH_CNT(hh_a, htable->hashmap_a); + *data_array = ALLOC(void *, node_cnt); + HASH_ITER(hh_a, htable->hashmap_a, node, tmp) { + (*data_array)[i] = node->data; + i++; + } + } + + return node_cnt; +} + +int rcu_hash_updating_flag(struct rcu_hash_table *htable) +{ + return htable->is_updating; } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 14cb586..835b206 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,11 +1,16 @@ include_directories(${PROJECT_SOURCE_DIR}/src/inc_internal) include_directories(${PROJECT_SOURCE_DIR}/deps) +include_directories(${PROJECT_SOURCE_DIR}/scanner) add_executable(maat_api_gtest maat_api_gtest.cpp) target_link_libraries(maat_api_gtest maat_frame_static gtest_static) -add_executable(adapter_hs_gtest adapter_hs_gtest.cpp) -target_link_libraries(adapter_hs_gtest maat_frame_static gtest_static) - add_executable(rcu_hash_gtest rcu_hash_gtest.cpp) -target_link_libraries(rcu_hash_gtest maat_frame_static gtest_static) \ No newline at end of file +target_link_libraries(rcu_hash_gtest maat_frame_static gtest_static) + +add_executable(maat_framework_gtest maat_framework_gtest.cpp) +target_link_libraries(maat_framework_gtest maat_frame_static gtest_static) + +file(COPY rule DESTINATION ./) +file(COPY table_info.conf DESTINATION ./) +file(COPY and_expr.conf DESTINATION ./) \ No newline at end of file diff --git a/test/maat_api_gtest.cpp b/test/maat_api_gtest.cpp index 6aba16b..82a64d8 100644 --- a/test/maat_api_gtest.cpp +++ b/test/maat_api_gtest.cpp @@ -1,6 +1,6 @@ -#include +#include "maat/maat.h" -#include "../include/maat/maat.h" +#include TEST(EQ_Test, Always_True) { EXPECT_EQ(1, 1); diff --git a/test/maat_framework_gtest.cpp b/test/maat_framework_gtest.cpp new file mode 100644 index 0000000..74a7d5f --- /dev/null +++ b/test/maat_framework_gtest.cpp @@ -0,0 +1,41 @@ +#include + +#include "maat/maat.h" +#include "maat_rule.h" +#include "maat_table_schema.h" +#include "maat_table_runtime.h" + +struct maat *g_maat_instance = NULL; +const char *table_info_path = "/home/liuwentan/project/maat-v4/test/table_info.conf"; +const char *rule_path = "/home/liuwentan/project/maat-v4/test/rule/full/index"; + +TEST(maat_scan_string, literal) { + struct table_schema_manager *table_schema_mgr = g_maat_instance->table_schema_mgr; + int table_id = table_schema_manager_get_table_id(table_schema_mgr, "HTTP_URL"); + char data[64] = "www.baidu.com"; + int result_array[5] = {0}; + size_t n_result_array = 0; + int ret = maat_scan_string(g_maat_instance, table_id, 0, data, strlen(data), result_array, &n_result_array, NULL); + EXPECT_EQ(ret, 0); + EXPECT_EQ(n_result_array, 1); + EXPECT_EQ(result_array[0], 101); +} + +int main(int argc, char ** argv) +{ + int ret=0; + ::testing::InitGoogleTest(&argc, argv); + + struct maat_options *opts = maat_options_new(); + maat_options_set_iris_full_dir(opts, rule_path); + + g_maat_instance = maat_new(opts, table_info_path); + EXPECT_NE(g_maat_instance, nullptr); + + ret=RUN_ALL_TESTS(); + + maat_free(g_maat_instance); + g_maat_instance = NULL; + + return ret; +} \ No newline at end of file diff --git a/test/rcu_hash_gtest.cpp b/test/rcu_hash_gtest.cpp index fe18201..204023d 100644 --- a/test/rcu_hash_gtest.cpp +++ b/test/rcu_hash_gtest.cpp @@ -1,16 +1,16 @@ -#include +#include "../src/inc_internal/rcu_hash.h" +#include "../include/utils.h" -#include "rcu_hash.h" -#include "maat_utils.h" +#include struct user_data { int id; char name[32]; }; -void data_free(void *data) +void data_free(void *user_ctx, void *data) { - + free(data); } TEST(rcu_hash_new, invalid_input_parameter) { @@ -18,27 +18,236 @@ TEST(rcu_hash_new, invalid_input_parameter) { EXPECT_EQ(htable, nullptr); } -TEST(rcu_hash_add, one) { +TEST(rcu_hash_add_one_node, single_thread) { + /* add one node to hash */ struct rcu_hash_table *htable = rcu_hash_new(data_free); EXPECT_NE(htable, nullptr); struct user_data *data = ALLOC(struct user_data, 1); data->id = 101; - char *name = "www.baidu.com"; + char name[64] = "www.baidu.com"; memcpy(data->name, name, strlen(name)); - char *key = "http_url"; + char key[64] = "http_url"; size_t key_len = strlen(key); + /* add to hash */ rcu_hash_add(htable, key, key_len, (void *)data); + /* find in hash before commit */ void *res = rcu_hash_find(htable, key, key_len); EXPECT_EQ(res, nullptr); + int ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 0); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + rcu_hash_commit(htable); + /* find in hash after commit */ res = rcu_hash_find(htable, key, key_len); EXPECT_NE(res, nullptr); + + struct user_data *res_data = (struct user_data *)res; + EXPECT_EQ(res_data->id, 101); + EXPECT_STREQ(res_data->name, "www.baidu.com"); + + ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 1); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + rcu_hash_free(htable); +} + +TEST(rcu_hash_add_multi_node, single_thread) { + /* add multi node to hash */ + struct rcu_hash_table *htable = rcu_hash_new(data_free); + EXPECT_NE(htable, nullptr); + + struct user_data *data0 = ALLOC(struct user_data, 1); + data0->id = 101; + char name0[64] = "www.baidu.com"; + memcpy(data0->name, name0, strlen(name0)); + char key0[64] = "http_url"; + size_t key0_len = strlen(key0); + rcu_hash_add(htable, key0, key0_len, (void *)data0); + + struct user_data *data1 = ALLOC(struct user_data, 1); + data1->id = 102; + char name1[64] = "127.0.0.1"; + memcpy(data1->name, name1, strlen(name1)); + char key1[64] = "http_host"; + size_t key1_len = strlen(key1); + rcu_hash_add(htable, key1, key1_len, (void *)data1); + + /* find in hash before commit */ + void *res = rcu_hash_find(htable, key0, key0_len); + EXPECT_EQ(res, nullptr); + res = rcu_hash_find(htable, key1, key1_len); + EXPECT_EQ(res, nullptr); + + int ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 0); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + rcu_hash_commit(htable); + + /* find in hash after commit */ + res = rcu_hash_find(htable, key0, key0_len); + EXPECT_NE(res, nullptr); + + struct user_data *res_data0 = (struct user_data *)res; + EXPECT_EQ(res_data0->id, 101); + EXPECT_STREQ(res_data0->name, "www.baidu.com"); + + res = rcu_hash_find(htable, key1, key1_len); + EXPECT_NE(res, nullptr); + + struct user_data *res_data1 = (struct user_data *)res; + EXPECT_EQ(res_data1->id, 102); + EXPECT_STREQ(res_data1->name, "127.0.0.1"); + + ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 2); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + rcu_hash_free(htable); +} + +TEST(rcu_hash_del_one_node, single_thread) { + /* case1: add and del before commit */ + struct rcu_hash_table *htable = rcu_hash_new(data_free); + EXPECT_NE(htable, nullptr); + + struct user_data *data = ALLOC(struct user_data, 1); + data->id = 101; + char name[64] = "www.baidu.com"; + memcpy(data->name, name, strlen(name)); + char key[64] = "http_url"; + size_t key_len = strlen(key); + + /* add to hash */ + rcu_hash_add(htable, key, key_len, (void *)data); + + /* find in hash before commit */ + void *res = rcu_hash_find(htable, key, key_len); + EXPECT_EQ(res, nullptr); + + int ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 0); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + rcu_hash_del(htable, key, key_len); + + rcu_hash_commit(htable); + + /* find in hash after commit */ + res = rcu_hash_find(htable, key, key_len); + EXPECT_EQ(res, nullptr); + + /* case2: add && commit, and del */ + struct user_data *data1 = ALLOC(struct user_data, 1); + data1->id = 102; + char name1[64] = "127.0.0.1"; + memcpy(data1->name, name1, strlen(name1)); + char key1[64] = "http_host"; + size_t key1_len = strlen(key1); + rcu_hash_add(htable, key1, key1_len, (void *)data1); + + /* add commit */ + rcu_hash_commit(htable); + + rcu_hash_del(htable, key1, key1_len); + + res = rcu_hash_find(htable, key1, key1_len); + EXPECT_NE(res, nullptr); + + struct user_data *res_data = (struct user_data *)res; + EXPECT_EQ(res_data->id, 102); + EXPECT_STREQ(res_data->name, "127.0.0.1"); + + ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 1); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 1); + /* delete commit */ + rcu_hash_commit(htable); + res = rcu_hash_find(htable, key1, key1_len); + EXPECT_EQ(res, nullptr); + + ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 0); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + rcu_hash_free(htable); +} + +TEST(rcu_hash_del_multi_node, single_thread) { + /* case1: add and del before commit */ + struct rcu_hash_table *htable = rcu_hash_new(data_free); + EXPECT_NE(htable, nullptr); + + struct user_data *data1 = ALLOC(struct user_data, 1); + data1->id = 101; + char name1[64] = "www.baidu.com"; + memcpy(data1->name, name1, strlen(name1)); + char key1[64] = "http_url"; + size_t key1_len = strlen(key1); + rcu_hash_add(htable, key1, key1_len, (void *)data1); + + struct user_data *data2 = ALLOC(struct user_data, 1); + data2->id = 102; + char name2[64] = "127.0.0.1"; + memcpy(data2->name, name2, strlen(name2)); + char key2[64] = "http_host"; + size_t key2_len = strlen(key2); + rcu_hash_add(htable, key2, key2_len, (void *)data2); + + /* find in hash before commit */ + void *res = rcu_hash_find(htable, key1, key1_len); + EXPECT_EQ(res, nullptr); + + int ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 0); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + /* add del, then commit */ + rcu_hash_del(htable, key1, key1_len); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 1); + + rcu_hash_commit(htable); + + /* find in hash after commit */ + res = rcu_hash_find(htable, key1, key1_len); + EXPECT_EQ(res, nullptr); + + res = rcu_hash_find(htable, key2, key2_len); + EXPECT_NE(res, nullptr); + + ret = rcu_hash_count(htable); + EXPECT_EQ(ret, 1); + + ret = rcu_hash_garbage_queue_len(htable); + EXPECT_EQ(ret, 0); + + rcu_hash_free(htable); } int main(int argc, char ** argv) diff --git a/test/rule/full/2022-11-24/HTTP_URL.000001 b/test/rule/full/2022-11-24/HTTP_URL.000001 new file mode 100644 index 0000000..57a9e99 --- /dev/null +++ b/test/rule/full/2022-11-24/HTTP_URL.000001 @@ -0,0 +1,2 @@ +0000000001 +101 1 www.baidu.com 0 0 0 1 \ No newline at end of file diff --git a/test/rule/full/index/full_config_index.000001 b/test/rule/full/index/full_config_index.000001 new file mode 100644 index 0000000..0087cff --- /dev/null +++ b/test/rule/full/index/full_config_index.000001 @@ -0,0 +1 @@ +HTTP_URL 1 /home/liuwentan/project/maat-v4/test/rule/full/2022-11-24/HTTP_URL.000001 \ No newline at end of file diff --git a/test/table_info.conf b/test/table_info.conf index 0da86c0..6e50c04 100644 --- a/test/table_info.conf +++ b/test/table_info.conf @@ -1,9 +1,9 @@ -#each column seperated by '\t' [ { "table_id":1, "table_name":"HTTP_URL", "table_type":"expr", + "scan_mode":"block", "item_id":1, "group_id":2, "rule": { @@ -11,21 +11,31 @@ "expr_type":4, "match_method":5, "is_hexbin":6, - "case_sensitive":7, - "is_valid":8 + "is_valid":7 } }, { "table_id":2, - "table_name":"TEST_IP_PLUGIN", + "table_name":"IP_PLUGIN_TABLE", "table_type":"ip_plugin", "item_id":1, - "group_id":2, "rule": { "ip_type":2, "start_ip":3, "end_ip":4, "is_valid":5 } + }, + { + "table_id":3, + "table_name":"PLUGIN_TABLE", + "table_type":"plugin", + "item_id":1, + "rule": { + "key":2, + "tag":3, + "is_valid":4, + "foreign":[6,8,10] + } } ] \ No newline at end of file diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index ba7ec61..8daca01 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -54,3 +54,19 @@ add_library(hyperscan_runtime_static STATIC IMPORTED GLOBAL) add_dependencies(hyperscan_runtime_static hyperscan) set_property(TARGET hyperscan_runtime_static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib64/libhs_runtime.a) set_property(TARGET hyperscan_runtime_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include) + +# ipmatcher-1.1 +ExternalProject_Add(ipmatcher PREFIX ipmatcher + URL ${CMAKE_CURRENT_SOURCE_DIR}/ipmatcher-v1.1.zip + CONFIGURE_COMMAND "" + BUILD_COMMAND make + INSTALL_COMMAND make DESTDIR= install + BUILD_IN_SOURCE 1) + +ExternalProject_Get_Property(ipmatcher INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(ipmatcher-static STATIC IMPORTED GLOBAL) +add_dependencies(ipmatcher-static ipmatcher) +set_property(TARGET ipmatcher-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/ipmatcher.a) +set_property(TARGET ipmatcher-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) diff --git a/vendor/ipmatcher-v1.1.zip b/vendor/ipmatcher-v1.1.zip new file mode 100644 index 0000000..48fe8e2 Binary files /dev/null and b/vendor/ipmatcher-v1.1.zip differ