* NEW [nanomq-nng] Ver 0.0.1 release

This commit is contained in:
jaylin@emqx.io 2020-09-02 12:38:54 +08:00
parent e075263a55
commit eca9dd8327
375 changed files with 95708 additions and 4 deletions

35
CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
# not finished yet #
cmake_minimum_required (VERSION 2.8.8)
project(nanomq-nng)
set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_LIST_DIR}/cmake"
)
option(NOLOG "build this project nolog" OFF)
if (NOLOG)
add_definitions(-DNOLOG)
endif (NOLOG)
#add_executable(nanomq-nng nanomq/nanomq.c)
#add_dependencies(nanomq-nng nng_h)
add_subdirectory(nng)
add_subdirectory(nanolib)
add_subdirectory(nanomq)
add_dependencies(nanomq nng)
add_dependencies(nanomq nanolib)

View File

@ -1,7 +1,83 @@
# nanomq
# NanoMQ
Nano MQTT Broker - A Ultra-light and Blazing-fast MQTT 5.0 Broker for IoT Edge.
Nano MQTT Broker
# Author
A light-weight and Blazing-fast MQTT 5.0 Broker for IoT Edge platform.
EMQ X Team.
## Features
1. Cost-effective on embedded platform.
2. Fully base on native POSIX. High Compatibility.
3. Pure C/C++ implementation. High portability.
4. Fully asynchronous I/O and multi-threading.
5. Good support for SMP.
6. Low latency & High handling capacity.
## QuickStart
1. Compile & Install
NanoMQ is base on NNG's asynchronous I/O threading model. With rewriting the TCP/SP part with self-added protocol: nano_tcp.
To build this whole project, you will need a C99 compatible & C++11 compiler and [CMake](http://www.cmake.org/) version 3.13 or newer.
Basically you just need to simply compile and install nanomq by:
$PROJECT_PATH/nanomq$ mkdir build & cd build
$PROJECT_PATH/nanomq/build$ cmake -G Ninja ..
$PROJECT_PATH/nanomq/build$ sudo ninja install
or you can limit threads by
cmake -G Ninja -DNNG_RESOLV_CONCURRENCY=1 -DNNG_NUM_TASKQ_THREADS=5 -DNNG_MAX_TASKQ_THREADS=5 ..
Please be aware that nanomq depends on nanolib & nng (MQTT ver)
both dependencies can be complied independently
$PROJECT_PATH/nanomq/nng/build$ cmake -G Ninja ..
$PROJECT_PATH/nanomq/nng/build$ ninja install
compile nanolib independently:
$PROJECT_PATH/nanolib/build$ cmake -G Ninja ..
$PROJECT_PATH/nanolib/build$ ninja install
In short future, We will implement a way to let nanomq support MQTT without damaging NNG's SP support.
Also rewrite CMake and MakeFile so that user can easily choose which ver of nng to base on.
TODO:
more features coming
===============================================
2. Usage:
#ongoing MQTT Broker
sudo ./nanomq broker start 'tcp://localhost:1883' &
#test POSIX message Queue
sudo ./nanomq broker mq start/stop
## Communties
You can Join us on Slack channel:
#nanomq: general usage
#nanomq-dev : for MQTT lover & developer
#nanomq-nng : for users & nng project.
More communities on github, slack, reddit, twitter, gitter, discord are coming soon.
## Authors
The EMQ X team.

66
nanolib/CMakeLists.txt Normal file
View File

@ -0,0 +1,66 @@
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
cmake_minimum_required (VERSION 2.8.8)
project(nanolib)
SET(CMAKE_CXX_FLAGS "-std=c++11 -O3")
set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
#aux_source_directory(. DIRSRCS)
include_directories(./include)
#add_subdirectory(./packet_parser)
# Call this from your own project's makefile.
# find_package(nng CONFIG REQUIRED)
# list of source files
set(libsrc hash.cc mqtt_db.c zmalloc.c)
# this is the "object library" target: compiles the sources only once
add_library(nanolib OBJECT ${libsrc})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE src)
# shared libraries need PIC
set_property(TARGET nanolib PROPERTY POSITION_INDEPENDENT_CODE 1)
#set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1)
# shared and static libraries built from the same object files
add_library(nano_shared SHARED $<TARGET_OBJECTS:nanolib>)
#add_library(nanolib_static STATIC $<TARGET_OBJECTS:nanolib>)
add_executable(test test.c)
target_link_libraries(test nano_shared)
install(TARGETS nano_shared EXPORT nanolibConfig
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
install(EXPORT nanolibConfig DESTINATION share/nanolib/cmake)
export(TARGETS ${PROJECT_NAME} FILE nanolibConfig.cmake)
add_custom_target(nanodist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)

29
nanolib/Makefile Normal file
View File

@ -0,0 +1,29 @@
CC = gcc
CFLAGS = -Wall -g -fPIC
INC = -I./inlcude
OBJ = mqtt_db.o zmalloc.o hash.o
DLIBS = -lnano
LDFLAGS = -L.
RPATH = -Wl,-rpath=.
DESTDIR = /usr/local/include/
all:nanolib test.c
$(CC) -o nano test.c $(CFLAGS) $(LDFLAGS) $(DLIBS) $(RPATH)
./nano
%.o:%.c $(INC)
$(CC) -c -o $@ $< $(CFLAGS)
hash.o:hash.cc
g++ -c -o hash.o hash.cc -Wall -g -fPIC
nanolib:$(OBJ)
$(CC) -shared -fPIC -o libnano.so $(OBJ) -lstdc++
rm -f $(OBJ)
.PHONY:clean
clean:
rm -f $(OBJ) libnano.so nano

257
nanolib/hash.cc Normal file
View File

@ -0,0 +1,257 @@
#include <unordered_map>
#include <cstring>
#include <mutex>
#include <cstdlib>
#include "include/dbg.h"
#include "include/hash.h"
using namespace std;
template<typename K, typename V>
class mqtt_hash {
public:
typedef typename unordered_map<K, V>::iterator iterator;
V &operator [](const K &_key)
{
lock_guard<mutex> lk(_mtx);
return hash_map[_key];
}
V get(const K &_key)
{
lock_guard<mutex> lk(_mtx);
return hash_map[_key];
}
void set(const K &_key, const V &_val)
{
lock_guard<mutex> lk(_mtx);
hash_map[_key] = _val;
}
void del(const K &_key)
{
lock_guard<mutex> lk(_mtx);
mqtt_hash<K, V>::iterator iter = hash_map.find(_key);
if (iter != hash_map.end()) {
hash_map.erase(iter);
}
}
bool find(const K &_key)
{
mqtt_hash<K, V>::iterator iter = hash_map.begin();
iter = hash_map.find(_key);
if (iter != hash_map.end()) {
return true;
}
return false;
}
private:
unordered_map<K, V> hash_map;
mutex _mtx;
};
mqtt_hash<int, char*> _mqtt_hash;
/*
**
*/
void push_val(int key, char *val)
{
_mqtt_hash[key] = val;
}
/*
**
*/
char *get_val(int key)
{
return _mqtt_hash.get(key);
}
/*
**
*/
void del_val(int key)
{
_mqtt_hash.del(key);
}
mqtt_hash<char *, topic_queue *> _topic_hash;
struct topic_queue *new_topic_queue(char *val)
{
struct topic_queue *tq = NULL;
int len = strlen(val);
tq = (struct topic_queue*)malloc(sizeof(struct topic_queue));
if (!tq) {
fprintf(stderr, "zmalloc: Out of memory\n");
fflush(stderr);
abort();
}
tq->topic = (char*)malloc(sizeof(char)*(len+1));
if (!tq->topic) {
fprintf(stderr, "zmalloc: Out of memory\n");
fflush(stderr);
abort();
}
memcpy(tq->topic, val, len);
tq->topic[len] = '\0';
tq->next = NULL;
return tq;
}
void delete_topic_queue(struct topic_queue *tq)
{
if (tq) {
if (tq->topic) {
log("delete topic:%s", tq->topic);
free(tq->topic);
tq->topic = NULL;
}
free(tq);
tq = NULL;
}
return;
}
/*
**
*/
void add_topic(char *id, char *val)
{
struct topic_queue *ntq = new_topic_queue(val);
struct topic_queue *tq = _topic_hash[id];
if (tq == NULL) {
_topic_hash[id] = ntq;
log("add_topic:%s",_topic_hash[id]->topic);
} else {
struct topic_queue *tmp = tq->next;
tq->next = ntq;
ntq->next = tmp;
log("add_topic:%s", tq->next->topic);
}
}
/*
**
*/
struct topic_queue *get_topic(char *id)
{
if (_topic_hash[id]) {
return _topic_hash[id];
}
return NULL;
}
/*
**
*/
void del_topic_one(char *id, char *topic)
{
struct topic_queue *tt = _topic_hash[id];
struct topic_queue *tb = NULL;
if (!strcmp(tt->topic, topic) && tt->next == NULL) {
_topic_hash.del(id);
delete_topic_queue(tt);
return;
}
if (!strcmp(tt->topic, topic)) {
_topic_hash[id] = tt->next;
delete_topic_queue(tt);
return;
}
while (tt) {
if (!strcmp(tt->topic, topic)) {
if (tt->next == NULL) {
tb->next = NULL;
} else {
tb->next = tt->next;
}
break;
}
tb = tt;
tt = tt->next;
}
delete_topic_queue(tt);
return;
}
/*
**
*/
void del_topic_all(char *id)
{
struct topic_queue *tq = _topic_hash[id];
_topic_hash.del(id);
while (tq) {
struct topic_queue *tt = tq;
tq = tq->next;
delete_topic_queue(tt);
}
return;
}
/*
**
*/
bool check_id(char *id)
{
return _topic_hash.find(id);
}
mqtt_hash<uint32_t, char *> _pipe_hash;
void add_pipe_id(uint32_t pipe_id, char *client_id)
{
_pipe_hash[pipe_id] = client_id;
log("add_pipe_id %d, client_id %s", pipe_id, _pipe_hash[pipe_id]);
return;
}
void del_pipe_id(uint32_t pipe_id)
{
#ifdef NOLOG
#else
char *res = _pipe_hash[pipe_id];
#endif
log("del_pipe_id %d, client_id %s", pipe_id, res);
_pipe_hash.del(pipe_id);
return;
}
char *get_client_id(uint32_t pipe_id)
{
return _pipe_hash[pipe_id];
}
bool check_pipe_id(uint32_t pipe_id)
{
return _pipe_hash.find(pipe_id);
}

66
nanolib/include/dbg.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
static inline char *nano_get_time()
{
char *buffer;
time_t now;
now = time(NULL);
buffer = ctime(&now);
if (!buffer)
return NULL;
if (buffer[strlen(buffer) - 1] == '\n')
buffer[strlen(buffer) - 1] = '\0';
return buffer;
}
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "[DEBUG] %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#ifdef NOLOG
#define log(M, ...)
#define log_info(M, ...)
#else
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) =========>> " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define log(M, ...) fprintf(stderr, "[INFO] %s (%lu:%s:%d) " M "\n",\
nano_get_time(), pthread_self(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

45
nanolib/include/hash.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef HASH_H
#define HASH_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct topic_queue {
char *topic;
struct topic_queue *next;
};
void push_val(int key, char *val);
char *get_val(int key);
void del_val(int key);
void add_topic(char *id, char *val);
struct topic_queue *get_topic(char *id);
void del_topic_one(char *id, char *topic);
void del_topic_all(char *id);
bool check_id(char *id);
void add_pipe_id(uint32_t pipe_id, char *client_id);
void del_pipe_id(uint32_t pipe_id);
char *get_client_id(uint32_t pipe_id);
bool check_pipe_id(uint32_t pipe_id);
#ifdef __cplusplus
}
#endif
#endif

131
nanolib/include/mqtt_db.h Normal file
View File

@ -0,0 +1,131 @@
#ifndef MQTT_DB_H
#define MQTT_DB_H
#include <stdbool.h>
#include <stdint.h>
typedef enum {UNEQUAL = 0, EQUAL = 1 } state;
struct client {
char *id;
void *ctxt;
struct client *next;
};
struct clients {
struct client* sub_client;
struct clients* down;
int len;
};
struct retain_msg {
uint8_t qos;
bool exist;
void *message;
};
struct db_node {
char *topic;
bool hashtag;
bool plus;
struct retain_msg *retain;
struct client *sub_client;
struct db_node *up;
struct db_node *down;
struct db_node *next;
};
/*
** for print_db_tree
*/
struct db_nodes {
struct db_node *node;
struct db_nodes *next;
};
/* if topic equal NULL, topic is finded */
struct topic_and_node {
char **topic;
bool hashtag;
struct db_node *node;
state t_state;
};
struct db_tree{
struct db_node *root;
// TODO
};
/* Create a db_tree */
void create_db_tree(struct db_tree **db);
/* Delete a db_tree */
void destory_db_tree(struct db_tree *db);
void print_db_tree(struct db_tree *db);
bool check_hashtag(char *topic_data);
bool check_plus(char *topic_data);
struct db_node *new_db_node(char *topic);
void delete_db_node(struct db_node *node);
void set_db_node(struct db_node *node, char **topic_queue);
/* Search node in db_tree*/
void search_node(struct db_tree *db, char **topic_queue, struct topic_and_node *tan);
/* Add node to db_tree */
void add_node(struct topic_and_node *input, struct client *id);
/* Delete node from db_tree when node does not have clientId */
void del_node(struct db_node *node);
void del_all(uint32_t pipe_id, void *db);
/* Free node memory */
void free_node(struct db_node *node);
/* Parsing topic from char* with '/' to char** */
char **topic_parse(char *topic);
struct db_node *find_next(struct db_node *node, bool *equal, char
**topic_queue);
void set_retain_msg(struct db_node *node, struct retain_msg *retain);
struct retain_msg *get_retain_msg(struct db_node *node);
struct clients *search_client(struct db_node *root, char **topic_queue);
bool check_client(struct db_node *node, char *id);
/* Delete client id. */
struct client *del_client(struct topic_and_node *input, char *id);
struct client *set_client(const char *id, void *ctxt);
void set_topic_and_node(char **topic_queue, bool hashtag, state t_state,
struct db_node *node, struct topic_and_node *tan);
struct client **iterate_client(struct clients * sub_clients, int *cols);
struct clients *new_clients(struct client *sub_client);
/* Add client id. */
void add_client(struct topic_and_node *input, struct client* sub_client);
/* A hash table, clientId or alias as key, topic as value */
char* hash_check_alias(int alias);
void hash_add_alias(int alias, char *topic_data);
void hash_del_alias(int alias);
#endif

17
nanolib/include/nanolib.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef NANOLIB_H
#define NANOLIB_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "mqtt_db.h"
#include "zmalloc.h"
#include "hash.h"
#include "dbg.h"
#endif

41
nanolib/include/zmalloc.h Normal file
View File

@ -0,0 +1,41 @@
/* zmalloc - total amount of allocated memory aware version of malloc()
*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* 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 __ZMALLOC_H
#define __ZMALLOC_H
#include <stddef.h>
void *zmalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
#endif /* _ZMALLOC_H */

944
nanolib/mqtt_db.c Normal file
View File

@ -0,0 +1,944 @@
#include <string.h>
#include <assert.h>
#include "include/mqtt_db.h"
#include "include/zmalloc.h"
#include "include/hash.h"
#include "include/dbg.h"
/*
** Create a db_tree
** Declare a global variable as func para
** struct db_tree *db;
*/
void create_db_tree(struct db_tree **db)
{
log_info("CREATE_DB_TREE");
*db = (struct db_tree *)zmalloc(sizeof(struct db_tree));
memset(*db, 0, sizeof(struct db_tree));
struct db_node *node = new_db_node("\0");
(*db)->root = node;
return;
}
/*
** Destory db tree
** destory all node & db_tree
*/
void destory_db_tree(struct db_tree *db)
{
log_info("DESTORY_DB_TREE");
/* TODO */
}
/*
** Print db_tree
** For debugging, you can output all node
** & node info
*/
#ifdef NOLOG
void print_db_tree(struct db_tree *db)
{
return;
}
#else
void print_db_tree(struct db_tree *db)
{
assert(db);
struct db_nodes *tmps = NULL;
struct db_nodes *tmps_end = NULL;
tmps = (struct db_nodes*)zmalloc(sizeof(struct db_nodes));
tmps->node = db->root;
int size = 1;
int len = size;
tmps->next = NULL;
tmps_end = tmps;
puts("-------------------DB_TREE---------------------");
puts("TOPIC | HASHTAG&PLUS | CLIENTID | FATHER_NODE");
puts("-----------------------------------------------");
while (tmps) {
size = 0;
while (len-- && tmps) {
struct db_node *tmp = tmps->node;
while (tmp) {
printf("\"%s\" ", tmp->topic);
printf("%d", tmp->hashtag);
printf("%d ", tmp->plus);
if (tmp->sub_client) {
printf("%s ", tmp->sub_client->id);
if (tmp->sub_client->next) {
printf("and more ");
} else {
printf("no more ");
}
} else {
printf("-- ");
}
if (tmp->up) {
if (strcmp("#", tmp->topic)) {
printf("\"%s\"\t ", tmp->up->topic);
} else {
printf("<-\t ");
}
} else {
printf("--\t");
}
if (tmp->down) {
// debug("sth new");
size++;
tmps_end->next = (struct db_nodes*)zmalloc(sizeof(struct db_nodes));
tmps_end = tmps_end->next;
tmps_end->node = tmp->down;
tmps_end->next = NULL;
}
if (tmp) {
// debug("tmp next");
tmp = tmp->next;
}
}
// debug("tmps next");
tmps = tmps->next;
}
printf("\n");
if (size == 0) {
break;
}
puts("----------------------------------------------");
len = size;
}
puts("-------------------DB_TREE---------------------\n");
}
#endif
/*
** Determine if the current topic data is "#"
** or not.
*/
bool check_hashtag(char *topic_data)
{
if (topic_data == NULL) {
return false;
}
return !strcmp(topic_data, "#");
}
/*
** Determine if the current topic data is "+"
** or not.
*/
bool check_plus(char *topic_data)
{
if (topic_data == NULL) {
return false;
}
return !strcmp(topic_data, "+");
}
struct db_node *new_db_node(char *topic)
{
struct db_node *node = NULL;
node = (struct db_node*)zmalloc(sizeof(struct db_node));
node->topic = (char*)zmalloc(strlen(topic)+1);
memcpy(node->topic, topic, strlen(topic)+1);
log("new_db_node %s", node->topic);
node->hashtag = false;
node->plus = false;
node->next = NULL;
node->down = NULL;
node->up = NULL;
node->sub_client = NULL;
return node;
}
void delete_db_node(struct db_node *node)
{
if (node) {
log("delete_db_node %s", node->topic);
if (node->topic) {
zfree(node->topic);
}
node->topic = NULL;
node->up = NULL;
node->next = NULL;
node->down = NULL;
zfree(node);
}
node = NULL;
}
void set_db_node(struct db_node *node, char **topic_queue)
{
if (node) {
debug("set_db_node topic is %s", *topic_queue);
node->down = new_db_node(*(topic_queue));
node->down->up = node;
node->down->next = NULL;
node->down->down = NULL;
} else {
/* TODO */
}
}
void insert_db_node(struct db_node *new_node, struct db_node *old_node)
{
log("insert_db_node %s", new_node->topic);
if (old_node->next != new_node) {
struct db_node *tmp_node = NULL;
tmp_node = old_node->next;
old_node->next = new_node;
new_node->next = tmp_node->next;
}
new_node->up = old_node->up ? old_node->up : old_node;
return;
}
/*
** Add nodes when the sub node is not found in the tree.
** You need do serach_node and set client before add_node.
** input is the result of search_node, id is the result
** of set_client.
*/
void add_node(struct topic_and_node *input, struct client *id)
{
log_info("ADD_NODE_START");
assert(input);
struct db_node *tmp_node = NULL;
struct db_node *new_node = NULL;
char **topic_queue = input->topic;
if (topic_queue == NULL) {
log("Topic_queue is NULL, no topic is needed add!");
return;
}
if (input->t_state == EQUAL) {
/*
** # is the last string in topic
*/
if (input->hashtag) {
input->node->hashtag = true;
if (input->node->next) {
new_node = new_db_node(*(++topic_queue));
insert_db_node(new_node, input->node);
} else {
input->node->next = new_db_node(*(++topic_queue));
insert_db_node(input->node->next, input->node);
new_node = input->node->next;
}
} else {
set_db_node(input->node, ++topic_queue);
if (check_plus(*(topic_queue))) {
debug("equal, plus is true");
input->node->plus = true;
}
new_node = input->node->down;
}
} else {
new_node = new_db_node(*topic_queue);
new_node->up = input->node;
if (check_plus(*topic_queue)) {
debug("unequal, plus is true");
input->node->plus = true;
tmp_node = input->node->down;
input->node->down = new_node;
new_node->next = tmp_node;
} else {
debug("unequal, plus is not true");
if (input->node->down->next) {
if (input->node->down->hashtag) {
tmp_node = input->node->down->next->next;
input->node->down->next->next = new_node;
new_node->next = tmp_node;
} else {
tmp_node = input->node->down->next;
input->node->down->next = new_node;
new_node->next = tmp_node;
}
} else {
input->node->down->next = new_node;
}
}
}
while (*(++topic_queue)) {
if (check_hashtag(*topic_queue)) {
debug("set hashtag is true");
new_node->hashtag = true;
/*
** TODO delete it or not
*/
if (new_node->next) {
tmp_node = new_db_node(*topic_queue);
tmp_node->up = new_node->up ? new_node->up : NULL;
tmp_node->down = NULL;
tmp_node->next = new_node->next;
new_node->next = tmp_node;
} else {
new_node->next = new_db_node(*topic_queue);
insert_db_node(new_node->next, new_node);
}
new_node = new_node->next;
} else {
if (check_plus(*topic_queue)) {
new_node->plus = true;
}
set_db_node(new_node, topic_queue);
new_node = new_node->down;
}
}
input->node = new_node;
if (id) {
new_node->sub_client = id;
}
return;
}
/* For duplicate node
TODO*/
void del_node(struct db_node *node)
{
assert(node);
log_info("DEL_NODE_START");
if (node->sub_client || node->down || node->hashtag) {
log("Node can't be deleted!");
return;
}
if (node->next) {
log("DELETE NODE AND NEXT!");
struct db_node *first = node->up->down;
if (first == node) {
node->up->plus = false;
node->up->down = node->next ? node->next : NULL;
} else {
while (first->next != node) {
first = first->next;
}
if (first->hashtag) {
first->hashtag = false;
first->next = first->next->next;
del_node(first);
} else {
first->next = first->next->next;
}
}
/* delete node */
delete_db_node(node);
} else {
log("DELETE NODE AND UP!");
if (node->up == NULL) {
log("Node can't be deleted!");
return;
} else if (node->up->next == node) {
node->up->hashtag = false;
node->up->next = NULL;
delete_db_node(node);
return;
}
struct db_node *tmp_node = node->up;
if (tmp_node->plus && tmp_node->down == node) {
tmp_node->plus = false;
}
if (tmp_node->down == node) {
tmp_node->down = NULL;
} else {
tmp_node = tmp_node->down;
while (tmp_node->next != node) {
tmp_node = tmp_node->next;
}
if (tmp_node->hashtag) {
tmp_node->hashtag = false;
tmp_node->next = tmp_node->next->next;
} else {
tmp_node->next = tmp_node->next->next;
}
}
delete_db_node(node);
/* delete node */
del_node(tmp_node);
/* iter */
/*
while (1) {
// TODO
}
*/
}
return;
}
void set_retain_msg(struct db_node *node, struct retain_msg *retain)
{
node->retain = retain;
}
struct retain_msg *get_retain_msg(struct db_node *node)
{
return node->retain;
}
/*
** Delete client.
*/
struct client *del_client(struct topic_and_node *input, char *id)
{
log_info("DEL_CLIENT_START");
assert(input && id);
struct client *client = input->node->sub_client;
struct client *before_client = NULL;
while (client) {
// debug("delete id is %s, client id is %s", id, client->id);
if (!strcmp(client->id, id)) {
log("delete client %s", id);
if (before_client) {
before_client->next = before_client->next->next;
return client;
} else {
before_client = input->node->sub_client;
if (input->node->sub_client->next) {
input->node->sub_client = input->node->sub_client->next;
} else {
input->node->sub_client = NULL;
}
return client;
}
}
before_client = client;
client = client->next;
}
if (client == NULL) {
log("no client is deleted!");
}
return NULL;
}
bool check_client(struct db_node *node, char *id)
{
assert(node && id);
struct client *sub = node->sub_client;
while (sub) {
if(!strcmp(sub->id, id)) {
log("clientID you find is in the tree node");
return false;
}
sub = sub->next;
}
return true;
}
struct client *set_client(const char *id, void *ctxt)
{
assert(id);
// assert(ctxt);
struct client *sub_client = NULL;
sub_client = (struct client*)zmalloc(sizeof(struct client));
memset(sub_client, 0, sizeof(struct client));
sub_client->id = (char*)zmalloc(strlen(id)+1);
memcpy(sub_client->id, id, strlen(id)+1);
sub_client->ctxt = ctxt;
// sub_client->next = NULL;
return sub_client;
}
/*
** Add client.
** Before add_client, you can call set_client to set the val of client
** & search_node to get the val of res where you can add_client
*/
void add_client(struct topic_and_node *input, struct client *sub_client)
{
log_info("ADD_CLIENT_START");
assert(input && sub_client);
if (input->node->sub_client == NULL) {
input->node->sub_client = sub_client;
log("add first client in this node");
} else {
struct client *client = input->node->sub_client;
if (!strcmp(client->id, sub_client->id)) {
log("clientID you find is in the tree node");
return;
}
while (client->next) {
if (strcmp(client->id, sub_client->id)) {
client = client->next;
} else {
log("clientID you find is in the tree node");
return;
}
}
log("add client %s", sub_client->id);
client->next = sub_client;
}
return;
}
void set_topic_and_node(char **topic_queue, bool hashtag, state t_state,
struct db_node *node, struct topic_and_node *tan)
{
tan->t_state = t_state;
tan->topic = topic_queue;
tan->hashtag = hashtag;
tan->node = node;
return;
}
/*
** search_node
** Pass the parameters db_tree and topic_queue, you will get the
** last node equal topic, if topic_queue matches exactly, tan->topic
** will be set NULL.
*/
void search_node(struct db_tree *db, char **topic_queue, struct topic_and_node *tan)
{
log_info("SEARCH_NODE_START");
assert(db->root && topic_queue);
struct db_node *node = db->root;
while (*topic_queue && node){
log("topic is: %s, node->topic is: %s", *topic_queue, node->topic);
if (strcmp(node->topic, *topic_queue)) {
bool equal = false;
node = find_next(node, &equal, topic_queue);
if (equal == false) {
log("searching unqual");
set_topic_and_node(topic_queue, false, UNEQUAL, node->up, tan);
break;
}
}
if (node->hashtag && check_hashtag(*(topic_queue+1))) {
log("searching # with hashtag");
set_topic_and_node(NULL, true, EQUAL, node->next, tan);
debug("%s", node->next->sub_client->id);
break;
} else if (check_hashtag(*(topic_queue+1))) {
log("searching # no hashtag");
set_topic_and_node(topic_queue, true, EQUAL, node, tan);
break;
}
log("searching no hashtag");
if (node->down && *(topic_queue+1)) {
// debug("continue");
topic_queue++;
node = node->down;
} else if (*(topic_queue+1) == NULL) {
// debug("topic_queue is NULL");
set_topic_and_node(NULL, false, EQUAL, node, tan);
// debug("node->topic = %s", node->topic);
break;
} else {
// debug("node is NULL");
set_topic_and_node(topic_queue, false, EQUAL, node, tan);
break;
}
}
return;
}
void del_all(uint32_t pipe_id, void *ptr)
{
char *client = get_client_id(pipe_id);
if (client == NULL) {
log("no client is found");
return;
}
log("--PID %d--CLID %s--", pipe_id, client);
struct db_tree *db = ptr;
if (client) {
if (check_id(client)) {
struct topic_queue *tq = get_topic(client);
while (tq) {
char **topic_queue = topic_parse(tq->topic);
struct topic_and_node *tan = NULL;
tan = (struct topic_and_node*)zmalloc(sizeof(struct topic_and_node));
search_node(db, topic_queue, tan);
debug("%s", tan->node->topic);
del_client(tan, client);
// struct client * cli = del_client(tan, client);
// TODO free cli
// cli = NULL;
del_node(tan->node);
char *tmp = NULL;
char **tt = topic_queue;
while (*topic_queue) {
tmp = *topic_queue;
topic_queue++;
zfree(tmp);
tmp = NULL;
}
zfree(tt);
topic_queue = NULL;
zfree(tan);
tan = NULL;
tq = tq->next;
}
del_topic_all(client);
del_pipe_id(pipe_id);
log("del all");
} else {
log("no topic can be found");
}
}
return;
}
void *get_client_info(struct db_node *node)
{
/* TODO */
return NULL;
}
struct client **iterate_client(struct clients *sub_clients, int *cols)
{
*cols = 1;
struct client **client_queue = NULL;
while (sub_clients) {
struct client *sub_client = sub_clients->sub_client;
while (sub_client) {
bool equal = false;
client_queue = (struct client**)zrealloc(client_queue, (*cols)*sizeof(struct client*));
for (int i = 0; i < (*cols)-1; i++) {
if (!strcmp(sub_client->id, client_queue[i]->id)) {
equal = true;
break;
}
}
if (equal == false) {
client_queue[(*cols)-1] = sub_client;
(*cols)++;
}
sub_client = sub_client->next;
}
sub_clients = sub_clients->down;
}
client_queue = (struct client**)zrealloc(client_queue, (*cols) * sizeof(struct client*));
client_queue[(*cols)-1] = NULL;
return client_queue;
}
struct clients *new_clients(struct client *sub_client)
{
struct clients *sub_clients = NULL;
if (sub_client) {
sub_clients = (struct clients*)zmalloc(sizeof(struct clients));
sub_clients->sub_client = sub_client;
sub_clients->down = NULL;
debug("first client is %s", sub_clients->sub_client->id);
}
return sub_clients;
}
struct db_node *find_next(struct db_node *node, bool *equal, char **topic_queue)
{
struct db_node *t = node;
if (node == NULL) {
return NULL;
}
while (t->next) {
t = t->next;
log("t->topic %s, topic_queue %s", t->topic,
*(topic_queue));
if (!strcmp(t->topic, *(topic_queue))) {
*equal = true;
break;
}
}
return t;
}
/*
** search_client
** When you use this func, the parameters you need to pass are the root
** node of the tree and the complete topic_queue. You will get all the
** subscribers to this topic.
*/
struct clients *search_client(struct db_node *root, char **topic_queue)
{
log_info("SEARCH_CLIENT_START");
assert(root && topic_queue);
struct clients *res = NULL;
struct clients *tmp = NULL;
tmp = (struct clients*)zmalloc(sizeof(struct clients));
memset(tmp, 0, sizeof(struct clients));
res = tmp;
struct db_node *node = root;
log("entry search");
while (*topic_queue && node) {
if (strcmp(node->topic, *topic_queue)) {
log("node->topic %s, topic_queue %s", node->topic, *topic_queue);
bool plus = false;
if (!strcmp(node->topic, "+")) {
if (*(topic_queue+1) == NULL) {
if (node->sub_client) {
log("add client in last +");
tmp->down = new_clients(node->sub_client);
tmp = tmp->down;
}
} else {
plus = true;
if (node->hashtag) {
log("Find the sign of #. Add it if sub_client is exist");
if (node->next->sub_client) {
tmp->down = new_clients(node->next->sub_client);
tmp = tmp->down;
}
}
}
}
bool equal = false;
node = find_next(node, &equal, topic_queue);
if (equal == false && plus == false) {
log("searching unqual");
return res;
}
if (equal == false && plus == true) {
node = node->up->down;
}
}
if (node->hashtag) {
log("Find the sign of #. Add it if sub_client of # is exist!");
if (node->next->sub_client) {
tmp->down = new_clients(node->next->sub_client);
tmp = tmp->down;
}
}
if (*(topic_queue+1) == NULL) {
log("Current node is the last one. Add it if sub_client is exist!");
tmp->down = new_clients(node->sub_client);
tmp = tmp->down;
return res;
}
if (node->plus) {
log("Find the sign of +");
if (*(topic_queue+2) == NULL) {
debug("When plus is the last one");
if (node->down->hashtag) {
log("Find the sign of #. Add it if sub_client of # is exist!");
if (node->down->next->sub_client) {
tmp->down = new_clients(node->down->next->sub_client);
tmp = tmp->down;
}
}
if (node->down->sub_client) {
tmp->down = new_clients(node->down->sub_client);
tmp = tmp->down;
}
bool equal = false;
struct db_node *t = find_next(node->down, &equal,
++topic_queue);
if (equal == false) {
log("searching unqual");
return res;
}
if (t->hashtag) {
log("Find the sign of #. Add it if sub_client of # is exist!");
if (t->next->sub_client) {
tmp->down = new_clients(t->next->sub_client);
tmp = tmp->down;
}
}
if (t->sub_client) {
tmp->down = new_clients(t->sub_client);
tmp = tmp->down;
}
return res;
} else if (node->down->down == NULL) {
log("topic is longer than tree, check hashtag");
if (node->down->hashtag) {
log("Find the sign of #. Add it if sub_client of # is exist!");
if (node->down->next->sub_client) {
tmp->down = new_clients(node->down->next->sub_client);
tmp = tmp->down;
}
}
bool equal = false;
struct db_node *t = find_next(node->down, &equal,
++topic_queue);
if (equal == false) {
log("searching unqual");
return res;
}
if (t->hashtag) {
log("Find the sign of #. Add it if sub_client of # is exist!");
if (t->next->sub_client) {
tmp->down = new_clients(t->next->sub_client);
tmp = tmp->down;
}
}
if (t != node->down) {
log("t->topic, %s, topic_queue ,%s", t->topic,
*(topic_queue));
tmp->down = search_client(t, topic_queue);
}
return res;
} else if (node->down->down && *(topic_queue+2)) {
log("continue");
char ** tmp_topic = topic_queue;
tmp->down = search_client(node->down, topic_queue+1);
while (tmp->down) {
tmp = tmp->down;
}
tmp->down = search_client(node->down->down, tmp_topic+2);
return res;
}
} else {
log("Find node no sign of + & #");
if (node->down && *(topic_queue+1)) {
log("continue");
topic_queue++;
node = node->down;
} else {
return res;
}
}
}
return res;
}
/* topic parsing */
char **topic_parse(char *topic)
{
assert(topic != NULL);
int row = 1;
int len = 2;
char **topic_queue = NULL;
char *before_pos = topic;
char *pos = NULL;
if ((strncmp("$share", before_pos, 6) != 0 && strncmp("$SYS", before_pos, 4)
!= 0)) {
topic_queue = (char**)zmalloc(sizeof(char*)*row);
topic_queue[row-1] = (char*)zmalloc(sizeof(char)*len);
memcpy(topic_queue[row-1], "\0", (len));
// strcpy(topic_queue[row-1], "");
// topic_queue[0][0] = '';
// topic_queue[row-1][len-1] = '\0';
}
while ((pos = strchr(before_pos, '/')) != NULL) {
if (topic_queue != NULL) {
topic_queue = (char**)zrealloc(topic_queue, sizeof(char*)*(++row));
} else {
topic_queue = (char**)zmalloc(sizeof(char*)*row);
}
len = pos-before_pos+1;
topic_queue[row-1] = (char*)zmalloc(sizeof(char)*len);
memcpy(topic_queue[row-1], before_pos, (len-1));
topic_queue[row-1][len-1] = '\0';
before_pos = pos+1;
}
len = strlen(before_pos);
if (topic_queue != NULL) {
topic_queue = (char**)zrealloc(topic_queue, sizeof(char*)*(++row));
} else {
topic_queue = (char**)zmalloc(sizeof(char*)*row);
}
topic_queue[row-1] = (char*)zmalloc(sizeof(char)*(len+1));
// strcpy(topic_queue[row-1], before_pos);
memcpy(topic_queue[row-1], before_pos, (len));
topic_queue[row-1][len] = '\0';
topic_queue = (char**)zrealloc(topic_queue, sizeof(char*)*(++row));
topic_queue[row-1] = NULL;
return topic_queue;
}
void hash_add_alias(int alias, char *topic_data)
{
assert(topic_data);
push_val(alias, topic_data);
}
char *hash_check_alias(int alias)
{
return get_val(alias);
}
void hash_del_alias(int alias)
{
del_val(alias);
}

View File

@ -0,0 +1,19 @@
CC = gcc
CFLAGS = -Wall
DEPS = packet_parser.h
OBJ = main.o packet_parser.o
%.o:%.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
nanomqlib:$(OBJ)
$(CC) -o nanolib $(OBJ)
rm -f $(OBJ)
#$(CC) -o nanolib main.o packet_parser.o
#$(CC) -o $@ $<
.PHONY:clean
clean:
rm -f $(OBJ) nanolib

View File

@ -0,0 +1,7 @@
#include "test.c"
int main(int argc, char *argv[])
{
test();
return 0;
}

View File

@ -0,0 +1,135 @@
#include "packet_parser.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
uint8_t packet_parse_uint8(struct mqtt_packet *packet)
{
assert(packet && packet->remaining_length >= packet->pos+1);
return packet->binary[packet->pos++];
}
uint16_t packet_parse_uint16(struct mqtt_packet *packet)
{
assert(packet && packet->remaining_length >= packet->pos+2);
uint16_t res = packet->binary[packet->pos++];
return (res << 8) + packet->binary[packet->pos++];
}
/* TODO judge endian */
//uint16_t packet_parse_uint16(struct mqtt_packet *packet)
//{
// assert(packet && packet->remaining_length >= packet->pos+2);
//
// uint16_t res = 0;
// memcpy((uint16_t*)&res, (uint16_t*)&(packet->binary[packet->pos]), sizeof(res));
// printf("res is: %#X\n", res);
// packet->pos+=2;
// return res;
//}
uint32_t packet_parse_uint32(struct mqtt_packet *packet)
{
assert(packet && packet->remaining_length >= packet->pos+4);
uint32_t res = packet->binary[packet->pos++];
if (res) {
while ((0xFF000000 & res) == 0) {
res = (res << 8) + packet->binary[packet->pos++];
}
} else {
int i = 4;
while (--i > 0) {
res = (res << 8) + packet->binary[packet->pos++];
}
}
return res;
}
void packet_parse_string(struct mqtt_packet *packet, char str[], uint32_t length)
{
assert(packet && packet->remaining_length >= packet->pos+length);
memcpy(str, &(packet->binary[packet->pos]), length);
packet->pos += length;
}
uint32_t packet_parse_var(struct mqtt_packet *packet)
{
assert(packet);
int i = 4;
uint32_t res = 0;
uint32_t multiplier = 1;
uint8_t byte;
do {
assert(packet->remaining_length >= packet->pos);
byte = packet->binary[packet->pos++];
res += (byte & 127) * multiplier;
multiplier *= 128;
} while (i-- && (byte & 128));
return res;
}
void packet_write_uint8(struct mqtt_packet *packet, uint8_t input)
{
assert(packet && packet->remaining_length >= packet->pos+1);
packet->binary[packet->pos++] = input;
}
void packet_write_uint16(struct mqtt_packet *packet, uint16_t input)
{
assert(packet && packet->remaining_length >= packet->pos+2);
packet->binary[packet->pos++] = (input & 0xFF00) >> 8;
packet->binary[packet->pos++] = input & 0x00FF;
}
void packet_write_uint32(struct mqtt_packet *packet, uint32_t input)
{
assert(packet && packet->remaining_length >= packet->pos+4);
packet->binary[packet->pos++] = (input & 0xFF000000) >> 24;
packet->binary[packet->pos++] = (input & 0x00FF0000) >> 16;
packet->binary[packet->pos++] = (input & 0x0000FF00) >> 8;
packet->binary[packet->pos++] = (input & 0x000000FF);
}
void packet_write_string(struct mqtt_packet *packet, char str[], uint32_t length)
{
assert(packet && packet->remaining_length >= packet->pos+length);
memcpy(&(packet->binary[packet->pos]), str, length);
packet->pos += length;
}
void packet_write_var(struct mqtt_packet *packet, uint32_t input)
{
assert(packet);
uint8_t byte;
do {
assert(packet->remaining_length >= packet->pos);
/* Can be optimized */
byte = input % 128;
input /= 128;
if (input > 0) {
byte |= 128;
}
packet->binary[packet->pos++] = byte;
} while (input);
}

View File

@ -0,0 +1,35 @@
#ifndef PACKET_PAESER
#define PACKET_PARSER
#include <string.h>
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef signed char int8_t;
struct mqtt_packet {
uint8_t *binary;
uint32_t pos;
uint32_t remaining_length;
};
uint8_t packet_parse_uint8(struct mqtt_packet *packet);
uint16_t packet_parse_uint16(struct mqtt_packet *packet);
uint32_t packet_parse_uint32(struct mqtt_packet *packet);
void packet_parse_string(struct mqtt_packet *packet, char str[], uint32_t length);
uint32_t packet_parse_var(struct mqtt_packet *packet);
void packet_write_uint8(struct mqtt_packet *packet, uint8_t input);
void packet_write_uint16(struct mqtt_packet *packet, uint16_t input);
void packet_write_uint32(struct mqtt_packet * packet, uint32_t intput);
void packet_write_string(struct mqtt_packet *packet, char str[], uint32_t length);
void packet_write_var(struct mqtt_packet *packet, uint32_t);
#endif

View File

@ -0,0 +1,237 @@
#include "packet_parser.h"
#include <stdio.h>
#include <stdlib.h>
/* =======================================================================
* TEST
* =======================================================================*/
static void uint8_write_read(uint8_t input, int remaining_length)
{
struct mqtt_packet packet;
memset(&packet, 0, sizeof(struct mqtt_packet));
packet.remaining_length = remaining_length;
packet.binary = (uint8_t *)malloc(remaining_length*sizeof(uint8_t));
packet_write_uint8(&packet, input);
packet.pos = 0;
uint8_t res = packet_parse_uint8(&packet);
if (input == res) {
printf("Bingle, uint8 %#X is OK!\n", input);
}
free(packet.binary);
packet.binary = NULL;
}
static void uint16_write_read(uint16_t input, uint32_t remaining_length)
{
struct mqtt_packet packet;
memset(&packet, 0, sizeof(struct mqtt_packet));
packet.remaining_length = remaining_length;
packet.binary = (uint8_t *)malloc(remaining_length*sizeof(uint8_t));
packet_write_uint16(&packet, input);
packet.pos = 0;
uint16_t res = packet_parse_uint16(&packet);
if (input == res) {
printf("Bingle, uint16 %#X is OK!\n", input);
}
free(packet.binary);
packet.binary = NULL;
}
static void uint32_write_read(uint32_t input, uint32_t remaining_length)
{
struct mqtt_packet packet;
memset(&packet, 0, sizeof(struct mqtt_packet));
packet.remaining_length = remaining_length;
packet.binary = (uint8_t *)malloc(remaining_length*sizeof(uint8_t));
packet_write_uint32(&packet, input);
packet.pos = 0;
uint32_t res = packet_parse_uint32(&packet);
if (input == res) {
printf("Bingle, uint32 %#X is OK!\n", input);
}
free(packet.binary);
packet.binary = NULL;
}
static void string_write_read(char *input, uint32_t remaining_length)
{
struct mqtt_packet packet;
memset(&packet, 0, sizeof(struct mqtt_packet));
packet.remaining_length = remaining_length;
packet.binary = (char*)malloc(sizeof(char)*remaining_length);
packet_write_string(&packet, input, remaining_length);
packet.pos = 0;
char str[remaining_length];
packet_parse_string(&packet, str, remaining_length);
printf("\npacket_parse_string output: %s\n",
str);
free(packet.binary);
packet.binary = NULL;
}
static void var_write_read(uint32_t input, uint32_t remaining_length)
{
struct mqtt_packet packet;
memset(&packet, 0, sizeof(packet));
packet.remaining_length = remaining_length;
packet.binary = (char*)malloc(sizeof(char)*remaining_length);
packet_write_var(&packet, input);
packet.pos = 0;
uint32_t res = packet_parse_var(&packet);
if (res == input) {
printf("Bingle, var length %#X is OK!\n", input);
}
free(packet.binary);
packet.binary = NULL;
}
static void TEST_uint8_write_read(void)
{
/* Empty packet */
// byte_write_read(NULL, 0);
uint8_t binary = 0;
/* 0 value */
// memset(binary, 0, sizeof(binary));
binary = 0x00;
uint8_write_read(binary, 1);
/* Middle */
// memset(binary, 0, sizeof(binary));
binary = 0x1F;
uint8_write_read(binary, 1);
/* 255 value */
// memset(binary, 0, sizeof(binary));
binary = 0xFF;
uint8_write_read(binary, 1);
}
static void TEST_uint16_write_read(void)
{
/* Empty packet */
// uint16_write_read(NULL, 0);
uint16_t binary = 0;
/* 0 value */
// memset(binary, 0, sizeof(binary));
binary = 0x0000;
uint16_write_read(binary, 2);
/* Endian check */
// memset(binary, 0, sizeof(binary));
binary = 0x38F3;
uint16_write_read(binary, 2);
/* 65,535 value */
// memset(binary, 0, sizeof(binary));
binary = 0xFFFF;
uint16_write_read(binary, 2);
}
static void TEST_uint32_write_read(void)
{
/* Empty packet */
// uint32_write_read(NULL, 0);
uint32_t binary = 0;
/* 0 value */
binary = 0x00000000;
uint32_write_read(binary, 4);
/* Endian check */
binary = 0x12345678;
uint32_write_read(binary, 4);
/* Biggest value */
binary = 0xFFFFFFFF;
uint32_write_read(binary, 4);
}
static void TEST_string_write_read(void)
{
// char *binary = "This is a string!";
char binary[18];
memset(binary, 0, sizeof(binary));
binary[0] = 'T';
binary[1] = 'h';
binary[2] = 'i';
binary[3] = 's';
binary[4] = ' ';
binary[5] = 'i';
binary[6] = 's';
binary[7] = ' ';
binary[8] = 'a';
binary[9] = ' ';
binary[10] = 's';
binary[11] = 't';
binary[12] = 'r';
binary[13] = 'i';
binary[14] = 'n';
binary[15] = 'g';
binary[16] = '!';
binary[17] = '\0';
string_write_read(binary, 18);
}
static void TEST_var_write_read(void)
{
uint32_t binary = 0;
/* 0 value */
binary = 0x00000000;
var_write_read(binary, 4);
/* Endian check */
binary = 0x12;
var_write_read(binary, 4);
/* Endian check */
binary = 0x1234;
var_write_read(binary, 4);
/* Endian check */
binary = 0x128456;
var_write_read(binary, 4);
/* Endian check */
binary = 0x12348678;
var_write_read(binary, 4);
/* Biggest value */
binary = 0x7FFFFFFF;
var_write_read(binary, 4);
}
void test(void)
{
TEST_uint8_write_read();
TEST_uint16_write_read();
TEST_uint32_write_read();
TEST_string_write_read();
TEST_var_write_read();
}

402
nanolib/test.c Normal file
View File

@ -0,0 +1,402 @@
#include <string.h>
#include "include/nanolib.h"
// struct clientId ID = {"150410"};
// struct db_node node = {"", &ID, NULL, NULL, NULL, NULL, 1, -1};
// struct db_tree db = {&node};
struct db_tree *db = NULL;
typedef enum{test_topic_parse = 0, test_search_node, test_add_node, test_del_node,
test_hash_alias, test_topic_hash, test_pipe_hash} test_state;
struct client ID1 = {"150429", NULL, NULL};
static void Test_topic_parse(void)
{
puts(">>>>>>>>>> TEST_TOPIC_PARSE <<<<<<<<");
// char *data = NULL;
// char *data = "lee";
// char *data = "$share/hom/jian";
char *data = "$shar/hom/jian";
printf("INPUT:%s\n", data);
char **res = topic_parse(data);
char *t = NULL;
char **tt = res;
while (*res) {
t = *res;
printf("RES: %s\n", *res);
res++;
zfree(t);
t = NULL;
}
zfree(tt);
res = NULL;
}
static void Test_search_node(void)
{
puts(">>>>>>>>>> TEST_SEARCH_NODE <<<<<<<<");
// char *data = "lee";
char *data = "lee/hom/jian";
create_db_tree(&db);
print_db_tree(db);
struct topic_and_node *res = NULL;
res = (struct topic_and_node*)zmalloc(sizeof(struct topic_and_node));
char **topic_queue = topic_parse(data);
search_node(db, topic_queue, res);
if (res->topic) {
printf("RES_TOPIC: %s\n", *(res)->topic);
}
if (res->node) {
printf("RES_NODE_STATE: %d\n", res->t_state);
}
if (res->node->sub_client) {
printf("RES_NODE_UP_ID: %s\n", res->node->sub_client->id);
}
zfree(res);
}
static void Test_add_node(void)
{
puts(">>>>>>>>>>> TEST_ADD_NODE <<<<<<<<<");
char *data = "a/bv/cv";
char *data1 = "a/b/c";
char *data2 = "lee/+/+";
char *data3 = "lee/hom/+";
struct client ID3 = {"150410", NULL, NULL};
struct client ID4 = {"150422", NULL, NULL};
struct client ID5 = {"150418", NULL, NULL};
struct topic_and_node *res = NULL;
res = (struct topic_and_node*)zmalloc(sizeof(struct topic_and_node));
char **topic_queue = topic_parse(data);
search_node(db, topic_queue, res);
add_node(res, &ID1);
print_db_tree(db);
search_node(db, topic_queue, res);
add_node(res, &ID1);
search_node(db, topic_queue, res);
if (check_client(res->node, ID1.id)) {
add_client(res, &ID1);
} else {
puts("2@@@@@@@@@@@@@@@@@@@@@@@@@@###@@@@@@@");
}
print_db_tree(db);
printf("RES_NODE_ID: %s\n", res->node->sub_client->id);
printf("RES_NODE_STATE: %d\n", res->t_state);
if (res->topic) {
printf("RES_TOPIC: %s\n", *(res->topic));
}
topic_queue = topic_parse(data1);
search_node(db, topic_queue, res);
add_node(res, &ID3);
print_db_tree(db);
search_node(db, topic_queue, res);
printf("RES_NODE_ID: %s\n", res->node->sub_client->id);
printf("RES_NODE_STATE: %d\n", res->t_state);
if (res->topic) {
printf("RES_TOPIC: %s\n", *(res->topic));
}
topic_queue = topic_parse(data2);
search_node(db, topic_queue, res);
add_node(res, &ID4);
print_db_tree(db);
search_node(db, topic_queue, res);
printf("RES_NODE_ID: %s\n", res->node->sub_client->id);
printf("RES_NODE_STATE: %d\n", res->t_state);
if (res->topic) {
printf("RES_TOPIC: %s\n", *(res->topic));
}
topic_queue = topic_parse(data3);
search_node(db, topic_queue, res);
if (res->topic) {
add_node(res, &ID5);
} else {
if (check_client(res->node, ID5.id)) {
add_client(res, &ID5);
}
}
print_db_tree(db);
search_node(db, topic_queue, res);
printf("RES_NODE_ID: %s\n", res->node->sub_client->id);
printf("RES_NODE_STATE: %d\n", res->t_state);
if (res->topic) {
printf("RES_TOPIC: %s\n", *(res->topic));
}
}
static void Test_del_node(void)
{
puts(">>>>>>>>>> TEST_DEL_NODE <<<<<<<<");
char *data = "lee/hom/jian/ll";
char *data1 = "+/#";
char *data2 = "+/+/+/+";
struct topic_and_node *res = NULL;
char **topic_queue = topic_parse(data);
res = (struct topic_and_node*)zmalloc(sizeof(struct topic_and_node));
search_node(db, topic_queue, res);
struct client ID2 = {"150410", NULL, NULL};
add_node(res, &ID2);
print_db_tree(db);
topic_queue = topic_parse(data1);
search_node(db, topic_queue, res);
add_node(res, &ID2);
print_db_tree(db);
topic_queue = topic_parse(data2);
search_node(db, topic_queue, res);
add_node(res, &ID2);
print_db_tree(db);
struct clients *res_clients = NULL;
struct client **client_queue = NULL;
int cols = 0;
topic_queue = topic_parse(data);
res_clients = search_client(db->root, topic_queue);
client_queue = iterate_client(res_clients, &cols);
while ((*client_queue) != NULL) {
puts("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
printf("%p %p \n", client_queue, *client_queue);
printf("RES: client_queue is:%s\n", (*client_queue)->id);
client_queue++;
}
// for (int i = 0; i < cols-1; i++) {
// puts("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ssss&&&&&&");
// printf("RES: client_queue is:%s\n", client_queue[i]->id);
// }
puts("----------------------------------------------------\nall:");
while (res_clients) {
struct client *sub_client = res_clients->sub_client;
while (sub_client) {
printf("RES: sub_client is:%s\n", sub_client->id);
sub_client = sub_client->next;
}
res_clients = res_clients->down;
}
// search_node(db, topic_queue, res);
// del_client(res, ID2.id);
// print_db_tree(db);
// del_node(res->node);
// print_db_tree(db);
// topic_queue = topic_parse(data1);
// search_node(db, topic_queue, res);
// del_client(res, ID2.id);
// print_db_tree(db);
// del_node(res->node);
// print_db_tree(db);
// topic_queue = topic_parse(data2);
// search_node(db, topic_queue, res);
// del_client(res, ID2.id);
// print_db_tree(db);
// del_node(res->node);
// print_db_tree(db);
}
static void Test_hash_alias(void)
{
puts(">>>>>>>>>>TEST_HASH_TABLE<<<<<<<<");
int i = 1;
int j = 2;
int k = 3;
char *topic1 = "topic1";
char *topic2 = "topic2";
char *topic3 = "topic3";
printf("INPUT: %d --> %s\n", i, topic1);
printf("INPUT: %d --> %s\n", j, topic2);
printf("INPUT: %d --> %s\n", k, topic3);
hash_add_alias(i, topic1);
hash_add_alias(i, topic2);
hash_add_alias(i, topic3);
hash_add_alias(j, topic2);
hash_add_alias(k, topic3);
char* t1 = hash_check_alias(i);
char* t2 = hash_check_alias(j);
char* t3 = hash_check_alias(k);
printf("RES: %s\n", t1);
printf("RES: %s\n", t2);
printf("RES: %s\n", t3);
hash_del_alias(i);
hash_del_alias(j);
hash_del_alias(k);
t1 = hash_check_alias(i);
t2 = hash_check_alias(j);
t3 = hash_check_alias(k);
if (t1) {
printf("RES: %s\n", t1);
}
if (t2) {
printf("RES: %s\n", t2);
}
if (t3) {
printf("RES: %s\n", t3);
}
}
static void Test_topic_hash(void)
{
char *id = "150410";
char *val = "lee/hom/jian";
char *val1 = "#";
char *val2 = "lee/#";
char *val3 = "a/b/c";
if (check_id(id)) {
puts("find");
} else {
puts("not find");
}
add_topic(id, val);
add_topic(id, val1);
add_topic(id, val2);
add_topic(id, val3);
if (check_id(id)) {
puts("find");
} else {
puts("not find");
}
struct topic_queue *res = get_topic(id);
while (res) {
printf("res: %s\n", res->topic);
res = res->next;
}
// del_topic_one(id, val1);
res = get_topic(id);
while (res) {
printf("res: %s\n", res->topic);
res = res->next;
}
// del_topic_all(id);
if (check_id(id)) {
puts("find");
} else {
puts("not find");
}
}
static void Test_pipe_hash(void)
{
uint32_t pipeid[] = {1, 2, 3, 4};
char* clientid[] ={"150429", "150410", "150428", "150418"};
for (int i = 0; i < 4; i++) {
add_pipe_id(pipeid[i], clientid[i]);
printf("get_client_id %s\n", get_client_id(pipeid[i]));
// del_pipe_id(pipeid[i]);
}
}
void test(test_state what)
{
switch(what) {
case test_topic_parse:
Test_topic_parse();
break;
case test_search_node:
Test_search_node();
break;
case test_add_node:
Test_add_node();
break;
case test_del_node:
Test_del_node();
break;
case test_hash_alias:
Test_hash_alias();
break;
case test_topic_hash:
Test_topic_hash();
break;
case test_pipe_hash:
Test_pipe_hash();
break;
default:
log("No this state");
break;
}
}
void help()
{
printf("please input the right num to conduct diff test\n");
printf(" test_topic_parse, 0\n");
printf(" test_search_node, 1\n");
printf(" test_add_node, 2\n");
printf(" test_del_node, 3\n");
printf(" test_hash_alias, 4\n");
printf(" test_topic_hash, 5\n");
printf(" test_pipe_hash, 6\n");
printf(" quit q\n");
printf(" help h\n");
}
int main(int argc, char *argv[])
{
puts("\n----------------TEST START------------------");
char str[5];
create_db_tree(&db);
help();
while (1) {
printf("input:");
scanf("%s", str);
if (!strcmp(str, "q")) {
break;
} else if (!strcmp(str, "h")) {
help();
continue;
}
int i = atoi(str);
test((test_state)i);
}
int i = 2;
puts("11111");
print_db_tree(db);
del_all(i, db);
puts("22222");
print_db_tree(db);
puts("---------------TEST FINISHED----------------\n");
return 0;
}

142
nanolib/zmalloc.c Normal file
View File

@ -0,0 +1,142 @@
/* zmalloc - total amount of allocated memory aware version of malloc()
*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* 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.
*/
/*
** Remove pthread by Ery Lee
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__sun)
#define PREFIX_SIZE sizeof(long long)
#else
#define PREFIX_SIZE sizeof(size_t)
#endif
#define increment_used_memory(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
used_memory += _n; \
} while(0)
#define decrement_used_memory(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
used_memory -= _n; \
} while(0)
static size_t used_memory = 0;
static void zmalloc_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE);
if (!ptr) zmalloc_oom(size);
#ifdef HAVE_MALLOC_SIZE
increment_used_memory(redis_malloc_size(ptr));
return ptr;
#else
*((size_t*)ptr) = size;
increment_used_memory(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
#endif
}
void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr;
if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
oldsize = redis_malloc_size(ptr);
newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom(size);
decrement_used_memory(oldsize);
increment_used_memory(redis_malloc_size(newptr));
return newptr;
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+PREFIX_SIZE);
if (!newptr) zmalloc_oom(size);
*((size_t*)newptr) = size;
decrement_used_memory(oldsize);
increment_used_memory(size);
return (char*)newptr+PREFIX_SIZE;
#endif
}
void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif
if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
decrement_used_memory(redis_malloc_size(ptr));
free(ptr);
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
decrement_used_memory(oldsize+PREFIX_SIZE);
free(realptr);
#endif
}
char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l);
memcpy(p,s,l);
return p;
}
size_t zmalloc_used_memory(void) {
size_t um;
um = used_memory;
return um;
}

38
nanomq/CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
cmake_minimum_required (VERSION 2.8.8)
project(nanomq)
SET(CMAKE_C_FLAGS "-std=c99")
SET(CMAKE_CXX_FLAGS "-std=c++11 -O3")
aux_source_directory(. DIRSRCS)
include_directories(./ ./apps ./include)
include_directories(${CMAKE_SOURCE_DIR}/nng/include/nng)
include_directories(${CMAKE_SOURCE_DIR}/nanolib/include)
add_subdirectory(apps)
set(PARALLEL 128 CACHE STRING "Parallelism (min 4, max 1000)")
# Call this from your own project's makefile.
find_package(nng CONFIG REQUIRED)
find_package(nanolib CONFIG REQUIRED)
#if (NANOLIB_FOUND)
# INCLUDE_DIRECTORIES(${NANOLIB_INCLUDE_DIR})
# TARGET_LINK_LIBRARIES(nanomq ${NANOLIB_LIBRARY})
#else (NANOLIB_FOUND)
# MESSAGE(FATAL_ERROR "nanolib library not found")
add_executable(nanomq nanomq.c cmd.c file.c process.c apps.c pub_handler.c subscribe_handle.c unsubscribe_handle.c)
#target_link_libraries(nanomq apps nano_shared)
target_link_libraries(nanomq apps nanolib)
target_link_libraries(nanomq nng)
target_compile_definitions(nanomq PRIVATE -DPARALLEL=${PARALLEL})

44
nanomq/Makefile Normal file
View File

@ -0,0 +1,44 @@
#
# Copyright (C) 2019 EMQX, Inc. Jaylin Yu
#
CC = gcc
CFLAGS += -Wall -fno-strict-aliasing -W -std=gnu99
CFLAGS += -I. -Ishared/ -Iconfig/ -Icheckin/ -Iapps/ `pkg-config --cflags `
OFLAGS += -Os
CPPFLAGS="-D PARALLEL=32 -I /usr/local/include"
LDFLAGS2="-L /usr/local/lib -lnng"
CC2="cc"
LDFLAGS += $(OFLAGS) -lrt `pkg-config`
EXTRA_CFLAGS += -DDEBUG_CONSOLE
# EXTRA_CFLAGS += -DDEBUG_FILE
EXTRA_CFLAGS += -DDEBUG_SYSLOG
# EXTRA_CFLAGS += -DDEBUG_TRACE
SRC_C = nanomq.c apps.c cmd.c process.c file.c const_string.c $(wildcard apps/*.c) $(wildcard config/*.c) $(wildcard shared/*.c) $(wildcard checkin/*.c)
SRC_H = ./include/nanomq.h ./include/apps.h $(wildcard apps/*.h) $(wildcard config/*.h) $(wildcard shared/*.h) $(wildcard checkin/*.h) $(wildcard include/*.h)
SRC_O = $(SRC_C:.c=.o)
ifneq ($(CONFIG_REGDBQUERY_STATIC_ONLY), y)
$(warning Makefile is valid!)
endif
ifeq ($(EMQ_DEBUG), y)
EXTRA_CFLAGS += -DDEBUG_CONSOLE
EXTRA_CFLAGS += -DDEBUG_SYSLOG
endif
CFLAGS +=
LDFLAGS += -Wl,-Bstatic -Wl,-Bdynamic
nanomq: $(SRC_O) $(SRC_H) Makefile
$(CC) -o $@ $(SRC_O) $(LDFLAGS)
.c.o:
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_CFLAGS) -MD -c $< -o $@
clean:
rm -f nanomq *.o *~ apps/*.o apps/*~ config/*.o config/*~ shared/*.o shared/*~
rm -f `find . -name '*.d' -print`

27
nanomq/README.adoc Normal file
View File

@ -0,0 +1,27 @@
NanoMQ base on NNG's AIO threading model. Rewriting the TCP/SP part with self-added protocol emq_tcp.
Basically you need to compile and install the nng first:
$PROJECT_PATH/nanomq/build$ cmake -G Ninja ..
or you can limit threads by
cmake -G Ninja -DNNG_RESOLV_CONCURRENCY=1 -DNNG_NUM_TASKQ_THREADS=2 -DNNG_MAX_TASKQ_THREADS=2 ..
$PROJECT_PATH/nanomq/build$ ninja
$PROJECT_PATH/nanomq/build$ ninja install
then compile nanomq independently:
$PROJECT_PATH/nanomq/emq/nanomq/build$ cmake -G Ninja ..
$PROJECT_PATH/nanomq/emq/nanomq/build$ ninja
In the future, We will trying to figure out a way to support MQTT without damaging NNG's SP support.
Also rewrite CMake and MakeFile so that user can compile nng-emq version with nanomq together.
===============================================
usage:
#ongoing MQTT Broker/ only a plain TCP server for now
sudo ./nanomq broker start 'tcp://localhost:1883'
#test POSIX message Queue
sudo ./nanomq broker mq start/stop

22
nanomq/apps.c Normal file
View File

@ -0,0 +1,22 @@
#include "include/apps.h"
#include "broker.h"
#include "mq.h"
#include <stdlib.h>
NANOMQ_APP(mq, mqcreate_debug, mqsend_debug, mqreceive_debug);
NANOMQ_APP(broker, broker_dflt, broker_start, NULL);
#if defined(NANO_DEBUG)
#endif
const struct nanomq_app *edge_apps[] = {
&nanomq_app_mq,
&nanomq_app_broker,
#if defined(NANO_DEBUG)
//&
#endif
NULL,
};

View File

@ -0,0 +1 @@
RESOLVE: SOURCE=/home/jaylin/CodeWarehouse/OpenWRT/sdk/EV_Router_SDK/package/libev BUILD=

View File

@ -0,0 +1,19 @@
aux_source_directory(. DIR_LIB_SRCS)
include_directories(../include)
include_directories(${CMAKE_SOURCE_DIR}/nng/include/nng)
include_directories(${CMAKE_SOURCE_DIR}/nanolib/include)
find_library(LIBRT rt)
find_package(nng CONFIG REQUIRED)
find_package(nanolib REQUIRED)
set(PARALLEL 128 CACHE STRING "Parallelism (min 4, max 1000)")
add_library (apps ${DIR_LIB_SRCS})
target_link_libraries(apps ${LIBRT})
target_link_libraries(apps nng)
#target_link_libraries(apps nano_shared)
target_link_libraries(apps nanolib)
target_compile_definitions(apps PRIVATE -DPARALLEL=${PARALLEL})

415
nanomq/apps/broker.c Normal file
View File

@ -0,0 +1,415 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <protocol/mqtt/mqtt_parser.h>
#include <nng.h>
#include <mqtt_db.h>
#include <hash.h>
#include "include/nanomq.h"
#include "include/pub_handler.h"
#include "include/subscribe_handle.h"
#include "include/unsubscribe_handle.h"
// Parallel is the maximum number of outstanding requests we can handle.
// This is *NOT* the number of threads in use, but instead represents
// outstanding work items. Select a small number to reduce memory size.
// (Each one of these can be thought of as a request-reply loop.) Note
// that you will probably run into limitations on the number of open file
// descriptors if you set this too high. (If not for that limit, this could
// be set in the thousands, each context consumes a couple of KB.)
// #ifndef PARALLEL
// #define PARALLEL 128
// #endif
#define PARALLEL 128
// The server keeps a list of work items, sorted by expiration time,
// so that we can use this to set the timeout to the correct value for
// use in poll.
void
fatal(const char *func, int rv)
{
fprintf(stderr, "%s: %s\n", func, nng_strerror(rv));
exit(1);
}
void transmit_msgs_cb(nng_msg *send_msg, emq_work *work, uint32_t *pipes)
{
work->state = SEND;
work->msg = send_msg;//FIXME should be encode message for each sub cilent due to different minimum QoS
nng_aio_set_msg(work->aio, send_msg);
work->msg = NULL;
nng_aio_set_pipeline(work->aio, pipes);
nng_ctx_send(work->ctx, work->aio);
}
/*objective: 1 input/output low latency
2 KV
3 tree
*/
void
server_cb(void *arg)
{
emq_work *work = arg;
nng_ctx ctx2;
nng_msg *msg;
nng_msg *smsg;
nng_pipe pipe;
int rv;
uint32_t when;
uint32_t *pipes = NULL;
// struct pipe_nng_msg *pipe_msgs = NULL;
reason_code reason;
uint8_t buf[2];
switch (work->state) {
case INIT:
debug_msg("INIT ^^^^^^^^^^^^^^^^^^^^^ \n");
work->state = RECV;
nng_ctx_recv(work->ctx, work->aio);
debug_msg("INIT!!\n");
break;
case RECV:
debug_msg("RECV ^^^^^^^^^^^^^^^^^^^^^ %d ^^^^\n", work->ctx.id);
if ((rv = nng_aio_result(work->aio)) != 0) {
debug_msg("ERROR: RECV nng aio result error: %d", rv);
nng_aio_wait(work->aio);
//break;
fatal("RECV nng_ctx_recv", rv);
}
msg = nng_aio_get_msg(work->aio);
if (msg == NULL) { //BUG
debug_msg("ERROR: RECV NULL msg");
//fatal("RECV NULL MSG", rv);
}
pipe = nng_msg_get_pipe(msg);
debug_msg("RECVIED %d %x\n", work->ctx.id, nng_msg_cmd_type(msg));
if (nng_msg_cmd_type(msg) == CMD_DISCONNECT) {
/**/
work->cparam = (conn_param *) nng_msg_get_conn_param(msg);
char *clientid = (char *) conn_param_get_clentid(work->cparam);
struct topic_and_node *tan = nng_alloc(sizeof(struct topic_and_node));
struct client *cli = NULL;
struct topic_queue *tq = NULL;
debug_msg("##########DISCONNECT (clientID:[%s])##########", clientid);
if (check_id(clientid)) {
tq = get_topic(clientid);
while (tq) {
if (tq->topic) {
search_node(work->db, topic_parse(tq->topic), tan);
if ((cli = del_client(tan, clientid)) == NULL) {
break;
}
del_pipe_id(pipe.id);
}
if (cli) {
del_node(tan->node);
debug_msg("Destroy CTX [%p] clientID: [%s]", cli->ctxt, cli->id);
destroy_sub_ctx(cli->ctxt, tq->topic); // only free work->sub_pkt
nng_free(cli, sizeof(struct client));
}
if (check_id(clientid)) {
tq = tq->next;
}
}
del_topic_all(clientid);
del_pipe_id(pipe.id);
nng_free(tan, sizeof(struct topic_and_node));
debug_msg("INHASH: clientid [%s] exist?: [%d]; pipeid [%d] exist?: [%d]",
clientid, (int) check_id(clientid), pipe.id, (int) check_pipe_id(pipe.id));
}
work->state = RECV;
nng_msg_free(msg);
work->msg = NULL;
nng_aio_abort(work->aio, 31);
nng_ctx_recv(work->ctx, work->aio);
break;
}
work->msg = msg;
work->state = WAIT;
debug_msg("RECV ********************* msg: %s %x******************************************\n",
(char *) nng_msg_body(work->msg), nng_msg_cmd_type(work->msg));
nng_sleep_aio(100, work->aio);
break;
case WAIT:
debug_msg("WAIT ^^^^^^^^^^^^^^^^^^^^^ %d ^^^^", work->ctx.id);
// We could add more data to the message here.
work->cparam = (conn_param *) nng_msg_get_conn_param(work->msg);
//debug_msg("WAIT %x %s %d pipe: %d\n", nng_msg_cmd_type(work->msg),
//conn_param_get_clentid(work->cparam), work->ctx.id, work->pid.id);
/*
if ((rv = nng_msg_append_u32(msg, msec)) != 0) {
fatal("nng_msg_append_u32", rv);
}
*/
//reply to client if needed. nng_send_aio vs nng_sendmsg? async or sync? BETTER sync due to realtime requirement
//TODO
if ((rv = nng_msg_alloc(&smsg, 0)) != 0) {
debug_msg("error nng_msg_alloc^^^^^^^^^^^^^^^^^^^^^");
}
if (nng_msg_cmd_type(work->msg) == CMD_PINGREQ) {
buf[0] = CMD_PINGRESP;
buf[1] = 0x00;
debug_msg("reply PINGRESP\n");
if ((rv = nng_msg_header_append(smsg, buf, 2)) != 0) {
debug_msg("error nng_msg_append^^^^^^^^^^^^^^^^^^^^^");
}
nng_msg_free(work->msg);
work->msg = smsg;
// We could add more data to the message here.
nng_aio_set_msg(work->aio, work->msg);
work->msg = NULL;
work->state = SEND;
nng_ctx_send(work->ctx, work->aio);
break;
} else if (nng_msg_cmd_type(work->msg) == CMD_SUBSCRIBE) {
pipe = nng_msg_get_pipe(work->msg);
work->pid = pipe;
debug_msg("get pipe!! ^^^^^^^^^^^^^^^^^^^^^ %d %d\n", pipe.id, work->pid.id);
work->sub_pkt = nng_alloc(sizeof(struct packet_subscribe));
if ((reason = decode_sub_message(work->msg, work->sub_pkt)) != SUCCESS ||
(reason = sub_ctx_handle(work)) != SUCCESS ||
(reason = encode_suback_message(smsg, work->sub_pkt)) != SUCCESS) {
debug_msg("ERROR IN SUB_HANDLE: %d", reason);
// TODO free sub_pkt
} else {
// success but check info
debug_msg("In sub_pkt: pktid:%d, topicLen: %d, topic: %s", work->sub_pkt->packet_id,
work->sub_pkt->node->it->topic_filter.len,
work->sub_pkt->node->it->topic_filter.str_body);
debug_msg("SUBACK: Header Len: %ld, Body Len: %ld. In Body. TYPE:%x LEN:%x PKTID: %x %x.",
nng_msg_header_len(smsg), nng_msg_len(smsg), *((uint8_t *) nng_msg_header(smsg)),
*((uint8_t *) nng_msg_header(smsg) + 1), *((uint8_t *) nng_msg_body(smsg)),
*((uint8_t *) nng_msg_body(smsg) + 1));
}
nng_msg_free(work->msg);
work->msg = smsg;
// We could add more data to the message here.
nng_aio_set_msg(work->aio, work->msg);
work->msg = NULL;
work->state = SEND;
nng_ctx_send(work->ctx, work->aio);
break;
//nng_send_aio
} else if (nng_msg_cmd_type(work->msg) == CMD_UNSUBSCRIBE) {
work->unsub_pkt = nng_alloc(sizeof(struct packet_unsubscribe));
if ((reason = decode_unsub_message(work->msg, work->unsub_pkt)) != SUCCESS ||
(reason = unsub_ctx_handle(work)) != SUCCESS ||
(reason = encode_unsuback_message(smsg, work->unsub_pkt)) != SUCCESS) {
debug_msg("ERROR IN UNSUB_HANDLE: %d", reason);
// TODO free unsub_pkt
} else {
// check info
debug_msg("In unsub_pkt: pktid:%d, topicLen: %d", work->unsub_pkt->packet_id,
work->unsub_pkt->node->it->topic_filter.len);
debug_msg("Header Len: %ld, Body Len: %ld.", nng_msg_header_len(smsg), nng_msg_len(smsg));
debug_msg("In Body. TYPE:%x LEN:%x PKTID: %x %x.", *((uint8_t *) nng_msg_header(smsg)),
*((uint8_t *) nng_msg_header(smsg) + 1), *((uint8_t *) nng_msg_body(smsg)),
*((uint8_t *) nng_msg_body(smsg) + 1));
}
nng_msg_free(work->msg);
work->msg = smsg;
// We could add more data to the message here.
nng_aio_set_msg(work->aio, work->msg);
work->msg = NULL;
work->state = SEND;
nng_ctx_send(work->ctx, work->aio);
break;
} else if (nng_msg_cmd_type(work->msg) == CMD_PUBLISH ||
nng_msg_cmd_type(work->msg) == CMD_PUBACK ||
nng_msg_cmd_type(work->msg) == CMD_PUBREC ||
nng_msg_cmd_type(work->msg) == CMD_PUBREL ||
nng_msg_cmd_type(work->msg) == CMD_PUBCOMP) {
//nng_mtx_lock(work->mutex);
if ((rv = nng_aio_result(work->aio)) != 0) {
debug_msg("WAIT nng aio result error: %d", rv);
fatal("WAIT nng_ctx_recv/send", rv);
//nng_aio_wait(work->aio);
//break;
}
#if DISTRIBUTE_DIFF_MSG
handle_pub(work, smsg, (void **) &work->pipe_msgs, NULL);
nng_msg_free(work->msg);
if (work->pipe_msgs != NULL) {
debug_msg("set pipeline[%d]: [%d]", work->pipe_msgs[0].index, work->pipe_msgs[0].pipe);
work->msg = work->pipe_msgs[0].msg;
nng_aio_set_msg(work->aio, work->msg);
work->msg = NULL;
work->state = SEND;
nng_aio_set_pipeline(work->aio, work->pipe_msgs[0].pipe);
work->index = 1;
nng_ctx_send(work->ctx, work->aio);
}
#else
handle_pub(work, smsg, pipes, transmit_msgs_cb);
#endif
if (work->state != SEND) {
nng_msg_free(smsg);
if (work->msg != NULL)
nng_msg_free(work->msg);
work->msg = NULL;
work->state = RECV;
nng_ctx_recv(work->ctx, work->aio);
}
//nng_mtx_unlock(work->mutex);
} else {
debug_msg("broker has nothing to do");
nng_msg_free(smsg);
if (work->msg != NULL)
nng_msg_free(work->msg);
work->msg = NULL;
work->state = RECV;
nng_ctx_recv(work->ctx, work->aio);
break;
}
break;
case SEND:
debug_msg("SEND ^^^^^^^^^^^^^^^^^^^^^ %d ^^^^\n", work->ctx.id);
if ((rv = nng_aio_result(work->aio)) != 0) {
debug_msg("SEND nng aio result error: %d", rv);
fatal("SEND nng_ctx_send", rv);
} else if ((smsg = nng_aio_get_msg(work->aio)) != NULL) {
nng_msg_free(smsg);
}
#if DISTRIBUTE_DIFF_MSG
if (work->pipe_msgs != NULL && work->pipe_msgs[work->index].pipe != 0) {
debug_msg("set pipeline[%d] %d: [%d]", work->pipe_msgs[work->index].index, work->index, work->pipe_msgs[work->index].pipe);
work->msg = work->pipe_msgs[work->index].msg;
nng_aio_set_msg(work->aio, work->msg);
work->msg = NULL;
work->state = SEND;
nng_aio_set_pipeline(work->aio, work->pipe_msgs[work->index].pipe);
work->index ++;
if (work->pipe_msgs[work->index].pipe == 0) {
free(work->pipe_msgs);
work->pipe_msgs = NULL;
}
nng_ctx_send(work->ctx, work->aio);
break;
} else {
work->msg = NULL;
work->state = RECV;
nng_ctx_recv(work->ctx, work->aio);
break;
}
#else
if (pipes != NULL) {
free(pipes);
pipes = NULL;
}
#endif
work->msg = NULL;
work->state = RECV;
nng_ctx_recv(work->ctx, work->aio);
break;
default:
fatal("bad state!", NNG_ESTATE);
break;
}
}
struct work *
alloc_work(nng_socket sock)
{
struct work *w;
int rv;
if ((w = nng_alloc(sizeof(*w))) == NULL) {
fatal("nng_alloc", NNG_ENOMEM);
}
if ((rv = nng_aio_alloc(&w->aio, server_cb, w)) != 0) {
fatal("nng_aio_alloc", rv);
}
//not pipe id; max id = uint32_t
if ((rv = nng_ctx_open(&w->ctx, sock)) != 0) {
fatal("nng_ctx_open", rv);
}
if ((rv = nng_mtx_alloc(&w->mutex)) != 0) {
fatal("nng_mtx_alloc", rv);
}
w->pipe_msgs = NULL;
w->index = 0;
w->state = INIT;
return (w);
}
// The server runs forever.
int
server(const char *url)
{
nng_socket sock;
nng_pipe pipe_id;
struct work *works[PARALLEL];
int rv;
int i;
// init tree
struct db_tree *db = NULL;
create_db_tree(&db);
/* Create the socket. */
rv = nng_nano_tcp0_open(&sock);
if (rv != 0) {
fatal("nng_nano_tcp0_open", rv);
}
debug_msg("PARALLEL: %d\n", PARALLEL);
for (i = 0; i < PARALLEL; i++) {
works[i] = alloc_work(sock);
works[i]->db = db;
nng_aio_set_dbtree(works[i]->aio, db);
// works[i]->pid = pipe_id;
}
if ((rv = nng_listen(sock, url, NULL, 0)) != 0) {
fatal("nng_listen", rv);
}
for (i = 0; i < PARALLEL; i++) {
server_cb(works[i]); // this starts them going (INIT state)
}
for (;;) {
nng_msleep(3600000); // neither pause() nor sleep() portable
}
}
int broker_start(int argc, char **argv)
{
int rc;
if (argc != 1) {
fprintf(stderr, "Usage: broker start <url>\n");
exit(EXIT_FAILURE);
}
rc = server(argv[0]);
exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
int broker_dflt(int argc, char **argv)
{
debug_msg("i dont know what to do here yet");
return 0;
}

32
nanomq/apps/broker.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef NANOMQ_BROKER_H
#define NANOMQ_BROKER_H
#define MQTT_VER 5
#include <nng/nng.h>
#include <nng/supplemental/util/platform.h>
#include <nng/protocol/mqtt/mqtt.h>
struct work {
int index;
enum { INIT, RECV, WAIT, SEND } state;
nng_aio *aio;
nng_msg *msg;
nng_ctx ctx;
nng_pipe pid;
nng_mtx *mutex;
struct db_tree *db;
conn_param *cparam;
struct pub_packet_struct *pub_packet;
struct packet_subscribe * sub_pkt;
struct packet_unsubscribe * unsub_pkt;
struct pipe_nng_msg *pipe_msgs;
};
typedef struct work emq_work;
int broker_start(int argc, char **argv);
int broker_dflt(int argc, char **argv);
#endif

126
nanomq/apps/mq.c Normal file
View File

@ -0,0 +1,126 @@
#include "mq.h"
#include "include/nanomq.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <mqueue.h>
#define FILEMODE (S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP)
struct mq_attr attr;
int mqcreate_debug(int argc, char **argv, char NANO_UNUSED(*extra_arg))
{
int c, flags;
mqd_t mqd;
flags = O_RDWR | O_CREAT;
while((c = getopt(argc, argv, "em:z:")) != -1) {//处理参数,带冒号的表示后面有参数的
switch(c) {
case 'e':
flags |= O_EXCL;
printf("the optind is :%d\n",optind);
break;
case 'm':
attr.mq_maxmsg = atol(optarg);
printf("the optind is :%d\n",optind);
break;
case 'z':
attr.mq_msgsize = atol(optarg);
printf("the optind is :%d\n",optind);
break;
}
}
if (optind != argc - 1) {
debug_msg("usage: mqcreate [-e] [-m maxmsg -z msgsize] <name>");
exit(0);
}
if ((attr.mq_maxmsg != 0 && attr.mq_msgsize == 0) || (attr.mq_maxmsg == 0 && attr.mq_msgsize !=0)){
debug_msg("must specify both -m maxmsg and -z msgsize");
exit(0);
}
mqd = mq_open(argv[optind], flags, FILEMODE, (attr.mq_maxmsg != 0) ? &attr : NULL);
debug_msg("%d %d", mqd, errno);
mq_close(mqd);
return 0;
}
int mqreceive_debug(int argc, char **argv, char NANO_UNUSED(*extra_arg))
{
int flags;
mqd_t mqd;
ssize_t n;
unsigned int prio;
char *buff;
struct mq_attr attr;
struct timespec abs_timeout;
flags = O_RDONLY;
clock_gettime(CLOCK_REALTIME, &abs_timeout);
abs_timeout.tv_sec+=4;
if (optind != argc-1) {
debug_msg("usage: mqreceive <name>");
exit(0);
}
mqd = mq_open(argv[optind], flags);
mq_getattr(mqd, &attr);
buff = malloc(attr.mq_msgsize);
//n = mq_receive(mqd, buff, attr.mq_msgsize, &prio);
debug_msg("%ld", abs_timeout.tv_sec);
n = (mq_timedreceive(mqd, buff, attr.mq_msgsize, &prio, &abs_timeout));
mq_close(mqd);
debug_msg("read %ld bytes, priority = %u buffer = %s\n",(long) n, prio, buff);
free(buff);
return 0;
}
int mqsend_debug(int argc, char **argv, char NANO_UNUSED(*extra_arg))
{
mqd_t mqd;
char *ptr;
unsigned int prio;
if (argc != 4) {
printf("usage: mqsend <name> <bytes> <priority>");
exit(0);
}
ptr = argv[2];
prio = atoi(argv[3]);
if ((mqd = mq_open(argv[1], O_WRONLY)) == -1) {
printf("open error");
exit(0);
}
//ptr = calloc(len, sizeof(char));
mq_send(mqd, ptr, strlen(argv[2]), prio);
return 0;
}
int dashboard_data_sync(int argc, char **argv, char NANO_UNUSED(*extra_arg))
{
mqd_t mqd;
char buff[64];
srand((unsigned)time(NULL));
if (argc != 1) {
printf("usage:sync <type:post/cache> ");
exit(0);
}
}

4
nanomq/apps/mq.h Normal file
View File

@ -0,0 +1,4 @@
extern int mqcreate_debug(int argc, char **argv, char *extra_arg);
extern int mqsend_debug(int argc, char **argv, char *extra_arg);
extern int mqreceive_debug(int argc, char **argv, char *extra_arg);
extern int dashboard_data_sync(int argc, char **argv, char *extra_arg);

301
nanomq/cmd.c Normal file
View File

@ -0,0 +1,301 @@
#include "include/nanomq.h"
#include "include/cmd.h"
#include "include/file.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
char *cmd_output_buff = NULL;
int cmd_output_len = 0;
int cmd_run_status(const char *cmd)
{
int error, pipes[2], stderr_fd = -1, ret = 0;
unsigned int sock_opts;
debug_msg("cmd = %s", cmd);
/*file_append_string("/tmp/cmd.log", (char *)cmd);
file_append_string("/tmp/cmd.log", "\n");*/
if (!cmd_output_buff)
cmd_output_buff = malloc(CMD_BUFF_LEN);
if (!cmd_output_buff)
return -1;
error = pipe(pipes);
if (error < 0) {
debug_msg("Warning - could not create a pipe to '%s': %s",
cmd, strerror(errno));
return -1;
}
/* save stderr */
stderr_fd = dup(STDERR_FILENO);
/* connect the commands output with the pipe for later logging */
dup2(pipes[1], STDERR_FILENO);
close(pipes[1]);
error = system(cmd);
/* copy stderr back */
dup2(stderr_fd, STDERR_FILENO);
close(stderr_fd);
memset(cmd_output_buff, 0, CMD_BUFF_LEN);
sock_opts = fcntl(pipes[0], F_GETFL, 0);
fcntl(pipes[0], F_SETFL, sock_opts | O_NONBLOCK);
cmd_output_len = read(pipes[0], cmd_output_buff, CMD_BUFF_LEN);
if (error < 0)
ret = error;
else if (WIFEXITED(error) && WEXITSTATUS(error) != 0)
ret = WEXITSTATUS(error);
close(pipes[0]);
return ret;
}
int cmd_run(const char *cmd)
{
int error, ret = 0;
error = cmd_run_status(cmd);
if (error != 0) {
debug_msg("Warning - command '%s' returned an error", cmd);
if (cmd_output_len > 0)
//debug_msg(" %s", cmd_output_buff);
debug_msg("warning");
ret = -1;
}
return ret;
}
int cmd_run_fd(int fd, const char *cmd)
{
int error, stderr_fd = -1, ret = 0;
debug_msg("fd = %i, cmd = %s", fd, cmd);
/*file_append_string("/tmp/cmd.log", (char *)cmd);
file_append_string("/tmp/cmd.log", "\n");*/
/* save stderr */
stderr_fd = dup(STDERR_FILENO);
/* connect the commands output with the pipe for later logging */
dup2(fd, STDERR_FILENO);
error = system(cmd);
/* copy stderr back */
dup2(stderr_fd, STDERR_FILENO);
close(stderr_fd);
if ((error < 0) || (WEXITSTATUS(error) != 0)) {
debug_msg("Warning - command '%s' returned an error", cmd);
ret = -1;
}
return ret;
}
int cmd_frun(const char *format, ...)
{
va_list args;
char *cmd;
int ret;
cmd = malloc(512);
if (!cmd)
return -1;
va_start(args, format);
vsprintf(cmd, format, args);
va_end(args);
ret = cmd_run(cmd);
free(cmd);
return ret;
}
int cmd_frun_fd(int fd, const char *format, ...)
{
va_list args;
char *cmd;
int ret;
cmd = malloc(512);
if (!cmd)
return -1;
va_start(args, format);
vsprintf(cmd, format, args);
va_end(args);
ret = cmd_run_fd(fd, cmd);
free(cmd);
return ret;
}
int cmd_pipe(const char *cmd)
{
int fd[2], ret;
pid_t pid;
debug_msg("cmd_pipe: cmd = %s", cmd);
ret = pipe(fd);
if (ret < 0) {
debug_msg("cmd_pipe: could not create pipe to '%s': %s", cmd, strerror(errno));
goto err;
}
pid = fork();
switch (pid) {
case -1:
goto err;
case 0:
close(fd[1]);
if (fd[0] != STDIN_FILENO) {
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
exit(EXIT_FAILURE);
close(fd[0]);
}
signal(SIGINT, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
exit(0);
default:
close(fd[0]);
return fd[1];
}
err:
return -1;
}
int cmd_fpipe(const char *format, ...)
{
va_list args;
char *cmd;
int ret;
cmd = malloc(512);
if (!cmd)
return -1;
va_start(args, format);
vsprintf(cmd, format, args);
va_end(args);
ret = cmd_pipe(cmd);
free(cmd);
return ret;
}
pid_t cmd_create_read_pipe(int *fd, const char *cmd, ...)
{
const char *cmd_run;
char **args = NULL;
int pipes[2];
va_list ap;
pid_t pid;
char *str;
int n;
va_start(ap, cmd);
if ((str = va_arg(ap, char *)) == NULL) {
cmd_run = "/bin/sh";
args = malloc(sizeof(char *) * 4);
args[0] = "/bin/sh";
args[1] = "-c";
args[2] = (char *)cmd;
args[3] = NULL;
} else {
cmd_run = cmd;
args = malloc(sizeof(char *));
args[0] = malloc(strlen(cmd) + 1);
strcpy(args[0], cmd);
n = 1;
do {
args = realloc(args, sizeof(char *) * (n + 1));
args[n] = malloc(strlen(str) + 1);
strcpy(args[n], str);
n++;
} while ((str = va_arg(ap, char *)) != NULL);
args = realloc(args, sizeof(char *) * (n + 1));
args[n] = NULL;
}
va_end(ap);
if (pipe(pipes) < 0) {
debug_msg("%s: count not create pipe to '%s' : %s",
cmd, strerror(errno));
goto err;
}
pid = fork();
switch (pid) {
case -1:
goto err;
case 0:
close(pipes[0]);
if (pipes[1] != STDOUT_FILENO) {
if (dup2(pipes[1], STDOUT_FILENO) != STDOUT_FILENO)
exit(EXIT_FAILURE);
close(pipes[1]);
}
signal(SIGPIPE, SIG_IGN);
execv(cmd_run, args);
free(args);
if (str)
free(str);
exit(0);
default:
close(pipes[1]);
*fd = dup(pipes[0]);
if(args)
free(args);
if(str)
free(str);
return pid;
}
err:
return -1;
}
void cmd_cleanup(void)
{
if (!cmd_output_buff)
return;
free(cmd_output_buff);
cmd_output_buff = NULL;
}

6
nanomq/const_string.c Normal file
View File

@ -0,0 +1,6 @@
/***
*
*
***/

10
nanomq/emq_parser.c Normal file
View File

@ -0,0 +1,10 @@
/**
* static MQTT parser lib
*
*
*
*
*
*
*/

536
nanomq/file.c Normal file
View File

@ -0,0 +1,536 @@
/***
*
***/
#include "include/nanomq.h"
#include "include/file.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <mtd/mtd-user.h>
#include <sys/ioctl.h>
#define NG_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
static char fpath_tmp[100];
int file_trunc_to_zero(const char *fpath)
{
int fd;
debug_msg("fpath = %s\n", fpath);
fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, NG_MODE);
if (fd >= 0)
close(fd);
return 0;
}
/*return 1 if exists*/
int file_exists(const char *fpath)
{
struct stat st;
int ret;
ret = stat(fpath, &st) == 0 ? 1 : 0;
debug_msg("%s: %i", fpath, ret);
return ret;
}
int file_is_symlink(const char *fpath)
{
int ret;
struct stat st;
ret = lstat(fpath, &st);
if (ret != 0)
return 0;
return S_ISLNK(st.st_mode);
}
int file_size(const char *fpath)
{
int ret;
struct stat st;
if (!file_exists(fpath))
return 0;
ret = stat(fpath, &st);
if (ret != 0)
return -1;
return st.st_size;
}
int file_create_symlink(const char *file_path, const char *link_path)
{
debug_msg("%s => %s", file_path, link_path);
return symlink(file_path, link_path);
}
int file_read_int(const char *fpath_fmt, ...)
{
va_list args;
char buff[10];
int fd, ret = 0;
va_start(args, fpath_fmt);
vsnprintf(fpath_tmp, sizeof(fpath_tmp), fpath_fmt, args);
va_end(args);
if (!file_exists(fpath_tmp))
goto out;
fd = open(fpath_tmp, O_RDONLY);
if (fd < 0)
goto out;
ret = read(fd, buff, sizeof(buff));
if (ret < 0)
goto close;
ret = strtol(buff, (char **)NULL, 10);
debug_msg("fpath = %s, pid = %d", fpath_tmp, ret);
close:
close(fd);
out:
return ret;
}
int file_read_string(const char *fpath, char *buff, int buff_len)
{
int fd, ret = 0;
if (!file_exists(fpath))
goto out;
fd = open(fpath, O_RDONLY);
if (fd < 0)
goto out;
ret = read(fd, buff, buff_len);
if (ret < 0)
goto close;
if (ret > 0)
buff[ret - 1] = '\0';
debug_msg("fpath = %s, string = %s", fpath, buff);
close:
close(fd);
out:
return ret;
}
int file_delete(const char *fpath)
{
if (!fpath)
return 0;
debug_msg("%s", fpath);
return unlink(fpath);
}
int file_create_dir(const char *fpath)
{
char *last_slash = NULL, *fpath_edit = NULL;
int ret = -1;
debug_msg("dir = %s", fpath);
if (fpath[strlen(fpath) - 1] != '/') {
fpath_edit = malloc(strlen(fpath) + 1);
if (!fpath_edit)
return -1;
strncpy(fpath_edit, fpath, strlen(fpath) + 1);
fpath_edit[strlen(fpath)] = '\0';
last_slash = strrchr(fpath_edit, '/');
/* not a single slash in the string ? */
if (!last_slash)
goto out;
*last_slash = '\0';
fpath = fpath_edit;
}
debug_msg("mkdir = %s", fpath);
ret = mkdir(fpath, 0777);
out:
free(fpath_edit);
return ret;
}
int file_write_int(int val, const char *fpath_fmt, ...)
{
int fd;
va_list args;
char buff[10], buff_len;
if (!fpath_fmt)
goto out;
va_start(args, fpath_fmt);
vsnprintf(fpath_tmp, sizeof(fpath_tmp), fpath_fmt, args);
va_end(args);
debug_msg("fpath = %s, int = %i", fpath_tmp, val);
buff_len = sprintf(buff, "%i\n", val);
fd = open(fpath_tmp, O_CREAT | O_WRONLY | O_TRUNC, NG_MODE);
if (fd < 0) {
debug_msg("Error - can't open file '%s' to write pid: %s",
fpath_tmp, strerror(errno));
goto error;
}
write(fd, buff, buff_len);
close(fd);
return 1;
out:
return 0;
error:
return -1;
}
int file_write_string(const char *fpath, const char *string)
{
int fd;
if (!fpath)
goto out;
debug_msg("fpath = %s, string = '%s'", fpath, string);
fd = open(fpath, O_CREAT | O_WRONLY | O_TRUNC, NG_MODE);
if (fd < 0) {
debug_msg("Error - can't open file '%s' to write string: %s",
fpath, strerror(errno));
goto error;
}
write(fd, string, strlen(string));
close(fd);
return 1;
out:
return 0;
error:
return -1;
}
int file_append_string(const char *fpath, const char *string_fmt, ...)
{
va_list args;
char string[100];
int fd;
if (!fpath)
goto out;
debug_msg("string_fmt = %s", string_fmt);
va_start(args, string_fmt);
vsnprintf(string, sizeof(string), string_fmt, args);
string[sizeof(string)-1] = '\0';
va_end(args);
debug_msg("fpath = %s, string = %s", fpath, string);
fd = open(fpath, O_CREAT | O_WRONLY | O_APPEND, NG_MODE);
if (fd < 0) {
debug_msg("Error - can't open file '%s' to append string: '%s'",
fpath, strerror(errno));
goto error;
}
write(fd, string, strlen(string));
close(fd);
return 1;
out:
return 0;
error:
return -1;
}
char *file_find_line(const char *fpath, const char *string)
{
char *line_ptr = NULL, *ptr;
FILE *fd;
size_t len = 0;
debug_msg("fpath = %s, string = %s", fpath, string);
if (!fpath)
goto out;
if (!string)
goto out;
fd = fopen(fpath, "r");
if (!fd)
goto out;
while (getline(&line_ptr, &len, fd) != -1) {
ptr = strcasestr(line_ptr, string);
if (!ptr)
continue;
goto found;
}
free(line_ptr);
fclose(fd);
out:
return NULL;
found:
fclose(fd);
return line_ptr;
}
int file_read_symlink_target(const char *fpath, char *buff, int buff_len)
{
int ret;
if (!file_is_symlink(fpath))
return -1;
memset(buff, 0, buff_len);
ret = readlink(fpath, buff, buff_len);
if (ret < 0)
return ret;
debug_msg("file %s links to %s", fpath, buff);
return 0;
}
int file_delete_symlink_target(const char *fpath)
{
int ret;
char path_buff[100];
ret = file_read_symlink_target(fpath, path_buff, sizeof(path_buff));
if (ret < 0)
return ret;
debug_msg("file %s", path_buff);
return file_delete(path_buff);
}
int file_is_directory(const char *fpath)
{
int ret;
struct stat st;
ret = lstat(fpath, &st);
if (ret != 0)
return 0;
debug_msg("fpath = %s, is_directory = %d", fpath, S_ISDIR(st.st_mode));
return S_ISDIR(st.st_mode);
}
int file_read_bin(const char *fpath, unsigned char **buff,
unsigned int offset, unsigned int length)
{
int fd, ret = 0;
if (!file_exists(fpath))
goto out;
fd = open(fpath, O_RDONLY);
if (fd < 0)
goto out;
*buff = malloc(length + 1);
if (!*buff)
goto close;
lseek(fd, offset, SEEK_SET);
ret = read(fd, *buff, length);
if (ret < 0)
goto close;
/*if (ret > 1)
*buff[ret - 1] = '\0';*/
debug_msg("fpath = %s, offset = %u, length = %u", fpath, offset,
length);
close:
close(fd);
out:
return ret;
}
unsigned int file_mtd_size_get(const char *fpath)
{
mtd_info_t mtd_info;
unsigned int res = 0;
int fd, ret;
debug_msg("fpath = %s", fpath);
if (!fpath)
goto out;
fd = open(fpath, O_RDONLY);
if (fd < 0)
goto out;
ret = ioctl(fd, MEMGETINFO, &mtd_info);
if (ret < 0)
goto out_close;
res = mtd_info.size;
out_close:
close(fd);
out:
return res;
}
int file_mtd_write(const char *fpath, unsigned char *buff, unsigned int buff_len)
{
mtd_info_t mtd_info;
erase_info_t ei;
unsigned int res = 0;
int fd, ret;
debug_msg("fpath = %s", fpath);
if (!fpath)
goto out;
fd = open(fpath, O_RDWR);
if (fd < 0)
goto out;
ret = ioctl(fd, MEMGETINFO, &mtd_info);
if (ret < 0)
goto out_close;
ei.length = mtd_info.erasesize;
for (ei.start = 0; ei.start < mtd_info.size; ei.start += mtd_info.erasesize) {
ioctl(fd, MEMUNLOCK, &ei);
ioctl(fd, MEMERASE, &ei);
}
res = write(fd, buff, buff_len);
out_close:
close(fd);
out:
return res;
}
int file_truncr_to_sep(const char *fpath, char *separator)
{
int fd, read_len;
FILE *fd_tmp;
char *line_ptr = NULL;
size_t line_len = 0, trunc_len = 0, trunc_len_tmp = 0;
debug_msg("fpath = %s, separator = %s", fpath, separator);
fd_tmp = fopen(fpath, "r");
if (!fd_tmp)
goto write_sep;
while ((read_len = getline(&line_ptr, &line_len, fd_tmp)) != -1) {
if (strncmp(line_ptr, separator, strlen(separator)) == 0) {
trunc_len += trunc_len_tmp;
trunc_len_tmp = 0;
}
trunc_len_tmp += read_len;
}
fclose(fd_tmp);
free(line_ptr);
write_sep:
fd = open(fpath, O_RDWR | O_CREAT, NG_MODE);
if (fd < 0)
return -1;
ftruncate(fd, trunc_len);
lseek(fd, trunc_len, SEEK_SET);
dprintf(fd, "%s\n", separator);
return fd;
}
int file_extract_int(const char *fpath)
{
char buff[16], *ptr;
int ret = 0;
if (!file_exists(fpath))
goto out;
file_read_string(fpath, buff, sizeof(buff));
ptr = strpbrk(buff, "0123456789");
if (!ptr)
ptr = buff;
ret = strtol(ptr, (char **)NULL, 10);
debug_msg("fpath = %s, int = %d", fpath, ret);
out:
return ret;
}
int file_append_int(const char *fpath, int value)
{
int fd;
char buff[10], buff_len;
if (!fpath)
goto out;
buff_len = sprintf(buff, "%i\n", value);
fd = open(fpath, O_CREAT | O_WRONLY | O_APPEND, NG_MODE);
if (fd < 0) {
debug_msg("Error - can't open file '%s' to append string: %s",
fpath, strerror(errno));
goto error;
}
write(fd, buff, buff_len);
close(fd);
return 1;
out:
return 0;
error:
return -1;
}

View File

@ -0,0 +1 @@
aux_source_directory(. DIR_LIB_SRCS)

19
nanomq/include/apps.h Normal file
View File

@ -0,0 +1,19 @@
#define APP_NAME_MAX 25
struct nanomq_app {
char name[APP_NAME_MAX];
int (*dflt)(int argc, char **argv);
int (*start)(int argc, char **argv);
int (*stop)(int argc, char **argv);
};
#define NANOMQ_APP(_name, _dflt, _start, _stop) \
const struct nanomq_app nanomq_app_##_name = { \
.name = #_name, \
.dflt = _dflt, \
.start = _start, \
.stop = _stop, \
}
extern const struct nanomq_app *edge_apps[];

27
nanomq/include/cmd.h Normal file
View File

@ -0,0 +1,27 @@
#include <sys/types.h>
#define CMD_RUN(cmd) do { \
ret = cmd_run(cmd); \
if (ret < 0) \
goto err; \
} while (0)
#define CMD_FRUN(fcmd, arg...) do { \
ret = cmd_frun(fcmd, ## arg); \
if (ret < 0) \
goto err; \
} while (0)
#define CMD_BUFF_LEN 1024
extern char *cmd_output_buff;
extern int cmd_output_len;
extern int cmd_run(const char *cmd);
extern int cmd_run_status(const char *cmd);
extern int cmd_frun(const char *format, ...);
extern int cmd_frun_fd(int fd, const char *format, ...);
int cmd_pipe(const char *cmd);
int cmd_fpipe(const char *format, ...);
pid_t cmd_create_read_pipe(int *fd, const char *cmd, ...);
void cmd_cleanup(void);

View File

@ -0,0 +1,2 @@
extern const char tmp_example[];

26
nanomq/include/file.h Normal file
View File

@ -0,0 +1,26 @@
int file_trunc_to_zero(const char *fpath);
int file_exists(const char *fpath);
int file_is_symlink(const char *fpath);
int file_size(const char *fpath);
int file_create_symlink(const char *file_path, const char *link_path);
int file_read_int(const char *fpath_fmt, ...);
int file_read_string(const char *fpath, char *buff, int buff_len);
int file_delete(const char *fpath);
int file_read_symlink_target(const char *fpath, char *buff, int buff_len);
int file_delete_symlink_target(const char *fpath);
int file_create_dir(const char *fpath);
int file_write_int(int val, const char *fpath_fmt, ...);
int file_write_string(const char *fpath, const char *string);
int file_append_string(const char *fpath, const char *string_fmt, ...);
char *file_find_line(const char *fpath, const char *string);
int file_is_directory(const char *fpath);
int file_read_bin(const char *fpath, unsigned char **buff,
unsigned int offset, unsigned int length);
unsigned int file_mtd_size_get(const char *fpath);
int file_mtd_write(const char *fpath, unsigned char *buff,
unsigned int buff_len);
int file_truncr_to_sep(const char *fpath, char *separator);
int file_append_int(const char *fpath, int value);
int file_extract_int(const char *fpath);

136
nanomq/include/nanomq.h Normal file
View File

@ -0,0 +1,136 @@
#define _GNU_SOURCE
#define DEBUG_FILE_PATH "/tmp/debug_nanomq.log"
// later expose on makefile
/**/
#if defined(NOLOG)
#undef DEBUG_CONSOLE
#undef DEBUG_FILE
#undef DEBUG_SYSLOG
#else
#define DEBUG_CONSOLE
#define DEBUG_FILE
#define DEBUG_SYSLOG
#endif
#undef LIBNANO_DEBUG
#if defined(DEBUG_CONSOLE) || defined(DEBUG_FILE) || defined(DEBUG_SYSLOG)
#define LIBNANO_DEBUG
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <syslog.h>
static inline char *nanomq_time_str()
{
char *buff;
time_t now;
now = time(NULL);
buff = ctime(&now);
if (!buff)
return NULL;
if (buff[strlen(buff) - 1] == '\n')
buff[strlen(buff) - 1] = '\0';
return buff;
}
#endif
#if defined(DEBUG_CONSOLE)
#define debug_console(fmt, arg...) do {\
char *_t = nanomq_time_str();\
fprintf(stderr, "%s %s: " fmt "\n", _t, __FUNCTION__, ## arg);\
} while (0)
#else
#define debug_console(fmt, arg...) do { } while (0)
#endif
#if defined(DEBUG_FILE)
#define debug_file(fmt, arg...) do {\
char *_t = nanomq_time_str();\
FILE *file = fopen(DEBUG_FILE_PATH, "a");\
fprintf(file, "%s [%i] %s: " fmt "\n", _t, getpid(), __FUNCTION__, ## arg);\
fclose(file);\
} while(0)
#else
#define debug_file(fmt, arg...) do { } while (0)
#endif
#if defined(DEBUG_SYSLOG)
#define debug_syslog(fmt, arg...) do {\
openlog("nanomq", LOG_PID, LOG_DAEMON | LOG_EMERG);\
syslog(0, "%s: " fmt, __FUNCTION__, ## arg);\
closelog();\
} while (0)
#else
#define debug_syslog(fmt, arg...) do { } while (0)
#endif
#if defined(LIBNANO_DEBUG)
#define debug_msg(fmt, arg...) do { \
debug_console(fmt, ## arg); \
debug_file(fmt, ## arg); \
debug_syslog(fmt, ## arg); \
} while (0)
#else
#define debug_msg(fmt, arg...) do { } while (0)
#endif
#define NNI_PUT16(ptr, u) \
do { \
(ptr)[0] = (uint8_t)(((uint16_t)(u)) >> 8u); \
(ptr)[1] = (uint8_t)((uint16_t)(u)); \
} while (0)
#define NNI_PUT32(ptr, u) \
do { \
(ptr)[0] = (uint8_t)(((uint32_t)(u)) >> 24u); \
(ptr)[1] = (uint8_t)(((uint32_t)(u)) >> 16u); \
(ptr)[2] = (uint8_t)(((uint32_t)(u)) >> 8u); \
(ptr)[3] = (uint8_t)((uint32_t)(u)); \
} while (0)
#define NNI_PUT64(ptr, u) \
do { \
(ptr)[0] = (uint8_t)(((uint64_t)(u)) >> 56u); \
(ptr)[1] = (uint8_t)(((uint64_t)(u)) >> 48u); \
(ptr)[2] = (uint8_t)(((uint64_t)(u)) >> 40u); \
(ptr)[3] = (uint8_t)(((uint64_t)(u)) >> 32u); \
(ptr)[4] = (uint8_t)(((uint64_t)(u)) >> 24u); \
(ptr)[5] = (uint8_t)(((uint64_t)(u)) >> 16u); \
(ptr)[6] = (uint8_t)(((uint64_t)(u)) >> 8u); \
(ptr)[7] = (uint8_t)((uint64_t)(u)); \
} while (0)
#define NNI_GET16(ptr, v) \
v = (((uint16_t)((uint8_t)(ptr)[0])) << 8u) + \
(((uint16_t)(uint8_t)(ptr)[1]))
#define NNI_GET32(ptr, v) \
v = (((uint32_t)((uint8_t)(ptr)[0])) << 24u) + \
(((uint32_t)((uint8_t)(ptr)[1])) << 16u) + \
(((uint32_t)((uint8_t)(ptr)[2])) << 8u) + \
(((uint32_t)(uint8_t)(ptr)[3]))
#define NNI_GET64(ptr, v) \
v = (((uint64_t)((uint8_t)(ptr)[0])) << 56u) + \
(((uint64_t)((uint8_t)(ptr)[1])) << 48u) + \
(((uint64_t)((uint8_t)(ptr)[2])) << 40u) + \
(((uint64_t)((uint8_t)(ptr)[3])) << 32u) + \
(((uint64_t)((uint8_t)(ptr)[4])) << 24u) + \
(((uint64_t)((uint8_t)(ptr)[5])) << 16u) + \
(((uint64_t)((uint8_t)(ptr)[6])) << 8u) + \
(((uint64_t)(uint8_t)(ptr)[7]))
#define NANO_UNUSED(x) (x)__attribute__((unused))

228
nanomq/include/packet.h Normal file
View File

@ -0,0 +1,228 @@
//
// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
//
// The Struct to store mqtt_packet.
#ifndef MQTT_PACKET_H
#define MQTT_PACKET_H
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
struct mqtt_string {
char * str_body;
int len;
};
typedef struct mqtt_string mqtt_string;
struct mqtt_string_node {
struct mqtt_string_node * next;
mqtt_string * it;
};
typedef struct mqtt_string_node mqtt_string_node;
struct mqtt_binary {
unsigned char * str_body;
int len;
};
typedef struct mqtt_binary mqtt_binary;
struct mqtt_str_pair {
char * str_key; // key
int len_key;
char * str_value; // value
int len_value;
};
typedef struct mqtt_str_pair mqtt_str_pair;
union Property_type{
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint32_t varint;
mqtt_binary binary;
mqtt_string str;
mqtt_str_pair strpair;
};
struct property {
uint8_t id;
union Property_type value;
struct property * next;
};
typedef struct property property;
struct mqtt_property {
uint32_t len;
uint32_t count;
struct property * property;
struct property * property_end;
};
typedef struct mqtt_property mqtt_property;
//variable header in mqtt_packet_subscribe
struct topic_with_option {
uint8_t qos: 2;
uint8_t no_local: 1;
uint8_t retain_as_publish: 1;
uint8_t retain_handling: 4; // !!!!!TODO actually 2 bits
mqtt_string topic_filter;
uint8_t reason_code;
};
typedef struct topic_with_option topic_with_option;
struct topic_node {
topic_with_option * it;
struct topic_node * next;
};
typedef struct topic_node topic_node;
struct packet_subscribe {
uint16_t packet_id;
union Property_type sub_id;
union Property_type user_property;
topic_node * node; // storage topic_with_option
};
typedef struct packet_subscribe packet_subscribe;
struct packet_unsubscribe {
uint16_t packet_id;
union Property_type user_property;
topic_node * node; // storage topic_with_option
};
typedef struct packet_unsubscribe packet_unsubscribe;
// variable header in mqtt_packet_connect
struct mqtt_packet_connect {
mqtt_string * proto_name;
uint8_t proto_ver;
bool is_bridge;
bool clean_start;
bool will_flag;
uint8_t will_qos;
bool will_retain;
uint16_t keep_alive;
mqtt_property * property;
bool has_username;
bool has_password;
};
struct mqtt_payload_connect {
mqtt_string * client_id; // 1-23 bytes[0-9a-zA-Z]
mqtt_property * will_property; //app msg included
mqtt_string * will_topic;
mqtt_binary * will_payload;
mqtt_string * username;
mqtt_binary * password; //
};
//variable header in mqtt_packet_connack
struct mqtt_packet_connack {
bool session_present;
uint8_t reason_code;
struct mqtt_property * property;
};
//struct mqtt_payload_connack {} = NULL;
//variable header in mqtt_packet_publish
struct mqtt_packet_publish {
mqtt_string * topic;
uint16_t packet_id;
mqtt_property * property;
};
struct mqtt_payload_publish {
mqtt_binary * msg;
};
//variable header in mqtt_apcket_puback
struct mqtt_packet_puback {
uint16_t packet_id;
uint8_t reason_code;
struct mqtt_property * property;
};
//struct mqtt_payload_puback {} = NULL;
//variable header in mqtt_packet_unsubscribe
struct mqtt_packet_unsubscribe {
uint16_t packet_id;
struct mqtt_property * property;
};
struct mqtt_payload_unsubscribe {
struct mqtt_string_node * topic_filter;
int count;
};
//variable header in mqtt_packet_unsuback
struct mqtt_packet_unsuback {
uint16_t packet_id;
struct mqtt_property * property;
};
struct mqtt_payload_unsuback {
mqtt_binary * reason_code_list; //each byte->topic_filter in order
};
// variable header in mqtt_packet_disconnect
struct mqtt_packet_disconnect {
uint8_t reason_code;
struct mqtt_property * property;
};
// struct mqtt_payload_disconnect {} = NULL;
//variable header in mqtt_packet_auth
struct mqtt_packet_auth {
uint8_t auth_reason_code;
struct mqtt_property * property;
};
// struct mqtt_payload_auth {} = NULL;
// typedef struct mqtt_packet_header mqtt_packet_header;
typedef struct mqtt_packet_connect mqtt_packet_connect;
typedef struct mqtt_packet_connack mqtt_packet_connack;
typedef struct mqtt_packet_publish mqtt_packet_publish;
typedef struct mqtt_packet_puback mqtt_packet_puback;
typedef struct mqtt_packet_unsubscribe mqtt_packet_unsubscribe;
typedef struct mqtt_packet_unsuback mqtt_packet_unsuback;
typedef struct mqtt_packet_disconnect mqtt_packet_disconnect;
typedef struct mqtt_packet_auth mqtt_packet_auth;
typedef struct mqtt_payload_connect mqtt_payload_connect;
typedef struct mqtt_payload_publish mqtt_payload_publish;
typedef struct mqtt_payload_unsubscribe mqtt_payload_unsubscribe;
typedef struct mqtt_payload_unsuback mqtt_payload_unsuback;
/*
// ctx of subscribe
struct ctx_sub {
mqtt_string id; // client id
// properties
union Property_type sub_id;
union Property_type user_property;
// topic with option
struct topic_with_option * topic_option;
// connect info
// struct ctx_connect * ctx_con;
};
typedef struct ctx_sub ctx_sub;
*/
#endif

6
nanomq/include/process.h Normal file
View File

@ -0,0 +1,6 @@
int process_is_alive(int pid);
int process_send_signal(int pid, int signal);
int pidgrp_send_signal(int pid, int signal);
int process_daemonize(void);
int process_create_child(int(*child_run)(void *), void *data);

View File

@ -0,0 +1,17 @@
#ifndef MQTT_PROPERTY_HANDLE_H
#define MQTT_PROPERTY_HANDLE_H
#include <string.h>
#include <nng/nng.h>
#include "include/packet.h"
int type_of_variable_property(uint8_t id);
void property_list_init(struct mqtt_property * list);
int property_list_insert(struct mqtt_property * list, uint8_t id, uint8_t * bin);
int property_list_free(struct mqtt_property * list);
struct property * property_list_head(struct mqtt_property * list);
struct property * property_list_end(struct mqtt_property * list);
struct property * property_list_get_element(struct mqtt_property * list, int pos);
struct property * property_list_find_element(struct mqtt_property * list, uint8_t id);
#endif

View File

@ -0,0 +1,124 @@
/**
* Created by Alvin on 2020/7/25.
*/
#ifndef NANOMQ_PUB_HANDLER_H
#define NANOMQ_PUB_HANDLER_H
#include <nng/nng.h>
#include <apps/broker.h>
#include "nng/protocol/mqtt/mqtt.h"
#define DISTRIBUTE_DIFF_MSG 1
typedef uint32_t variable_integer;
struct variable_string {
uint32_t str_len;
char *str_body;
};
struct variable_binary {
uint32_t data_len;
uint8_t *data;
};
//MQTT Fixed header
struct fixed_header {
//flag_bits
uint8_t retain: 1;
uint8_t qos: 2;
uint8_t dup: 1;
//packet_types
mqtt_control_packet_types packet_type: 4;
//remaining length
uint32_t remain_len;
};
struct property_u8 {
bool has_value; //false: no value;
uint8_t value;
};
struct property_u16 {
bool has_value; //false: no value;
uint16_t value;
};
struct property_u32 {
bool has_value; //false: no value;
uint32_t value;
};
//Special for publish message data structure
union property_content {
struct {
struct property_u8 payload_fmt_indicator;
struct property_u32 msg_expiry_interval;
struct property_u16 topic_alias;
struct variable_string response_topic;
struct variable_binary correlation_data;
struct variable_string user_property;
struct property_u32 subscription_identifier;
struct variable_string content_type;
} publish;
struct {
struct variable_string reason_string;
struct variable_string user_property;
} pub_arrc, puback, pubrec, pubrel, pubcomp;
};
//Properties
struct properties {
uint32_t len; //property length, exclude itself,variable byte integer;
// struct property prop_content[PUBLISH_PROPERTIES_TOTAL];
union property_content content;
};
//MQTT Variable header
union variable_header {
struct {
uint16_t packet_identifier;
struct variable_string topic_name;
struct properties properties;
} publish;
struct {
uint16_t packet_identifier;
reason_code reason_code: 8;
struct properties properties;
} pub_arrc, puback, pubrec, pubrel, pubcomp;
};
struct mqtt_payload {
uint8_t *payload;
uint32_t payload_len;
};
struct pub_packet_struct {
struct fixed_header fixed_header;
union variable_header variable_header;
struct mqtt_payload payload_body;
};
struct pipe_nng_msg {
uint32_t pipe;
uint32_t index;
uint8_t qos;
nng_msg *msg; //nng_msg
};
typedef void (*transmit_msgs)(nng_msg *, emq_work *, uint32_t *);
typedef void (*handle_client)(struct client *sub_client, void **pipes, uint32_t *total, void *packet);
void handle_pub(emq_work *work, nng_msg *send_msg, void **pipes, transmit_msgs tx_msgs);
bool encode_pub_message(nng_msg *dest_msg, struct pub_packet_struct *dest_pub_packet, const emq_work *work);
reason_code decode_pub_message(emq_work *work);
void foreach_client(struct clients *sub_clients, void **pipe_content, uint32_t *totals, void *packet,
handle_client handle_cb);
#endif //NNG_PUB_HANDLER_H

View File

@ -0,0 +1,14 @@
#ifndef MQTT_SUBSCRIBE_HANDLE_H
#define MQTT_SUBSCRIBE_HANDLE_H
#include <nng/nng.h>
#include "include/packet.h"
#include "apps/broker.h"
uint8_t decode_sub_message(nng_msg *, packet_subscribe *);
uint8_t encode_suback_message(nng_msg *, packet_subscribe *);
uint8_t sub_ctx_handle(emq_work *);
void destroy_sub_ctx(void *, char *);
// uint8_t subscribe_handle(nng_msg *);
#endif

View File

@ -0,0 +1,13 @@
#ifndef MQTT_UNSUBSCRIBE_HANDLE_H
#define MQTT_UNSUBSCRIBE_HANDLE_H
#include <nng/nng.h>
#include "include/packet.h"
#include "apps/broker.h"
uint8_t decode_unsub_message(nng_msg *, packet_unsubscribe *);
uint8_t encode_unsuback_message(nng_msg *, packet_unsubscribe *);
uint8_t unsub_ctx_handle(emq_work *);
#endif // MQTT_UNSUBSCRIBE_HANDLE_H

5
nanomq/include/version.h Normal file
View File

@ -0,0 +1,5 @@
#define FW_EV_VER_MAJOR 6
#define FW_EV_VER_MINOR 3
#define FW_EV_VER_PATCH 0
#define FW_EV_VER_ID_SHORT "3"
#define FW_EV_VER_ID_LONG "100"

158
nanomq/nanomq.c Normal file
View File

@ -0,0 +1,158 @@
#include "include/apps.h"
#include "include/version.h"
#include "include/process.h"
#include "include/cmd.h"
#include "include/const_strings.h"
#include "include/nanomq.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define NANO_APP_NAME "nanomq"
#define NANO_BRAND "EMQ X Edge Computing Kit"
#define NANO_DEBUG
static void print_version(void)
{
printf("%s v.01-%s\n", NANO_BRAND, FW_EV_VER_ID_SHORT);
printf("Copyright (C) 2012-2020 EMQ X Jaylin.\n");
printf("\n");
}
static int print_avail_apps(void)
{
#if defined(NANO_DEBUG)
const struct nanomq_app **nano_app;
printf("available applications:\n");
for (nano_app = edge_apps; *nano_app; ++nano_app)
printf(" * %s\n", (*nano_app)->name);
#endif
print_version();
return 1;
}
#if !defined(DEBUG_TRACE)
static int check_trace(char *name)
{
int pid, traced;
switch(pid = fork()) {
case 0:
pid = getppid();
traced = ptrace(PTRACE_ATTACH, pid, 0, 0);
if (!traced) {
process_send_signal(pid, SIGCONT);
_exit(EXIT_SUCCESS);
}
perror(name);
process_send_signal(pid, SIGKILL);
goto err;
case -1:
break;
default:
if (pid == waitpid(pid, 0, 0))
return EXIT_SUCCESS;
break;
}
perror(name);
err:
return -1;
}
#else
static int check_trace(char DASH_UNUSED(*name))
{
return 0;
}
#endif
static int handle_app(int res)
{
cmd_cleanup();
return res;
}
int main(int argc, char **argv)
{
const struct nanomq_app **nano_app;
char *app_name;
int ret;
ret = check_trace(argv[0]);
if (ret < 0)
return EXIT_FAILURE;
if ((argc > 1) && (strlen(argv[1]) > 1) &&
(argv[1][0] == '-') && (argv[1][1] == 'v')) {
print_version();
return EXIT_SUCCESS;
}
app_name = strrchr(argv[0], '/');
debug_msg("argv %s %s app_name %s", argv[0],argv[1], app_name);
app_name = (app_name ? app_name + 1 : argv[0]);
debug_msg("argv %s %s app_name %s", argv[0],argv[1], app_name);
if (strncmp(app_name, NANO_APP_NAME, APP_NAME_MAX) == 0) {
debug_msg("argc : %d", argc);
if (argc == 1)
return print_avail_apps();
app_name = argv[1];
argv++;
argc--;
}
for (nano_app = edge_apps; *nano_app; ++nano_app)
if (strncmp(app_name, (*nano_app)->name, APP_NAME_MAX) == 0)
break;
if (!(*nano_app)) {
printf("Error - the application '%s' was not found\n", app_name);
return EXIT_FAILURE;
}
if (argc < 2) {
if ((*nano_app)->dflt)
return handle_app((*nano_app)->dflt(argc - 1, argv + 1));
printf("Error - not enough arguments to run %s\n",
app_name);
goto err_param;
}
if ((strcmp(argv[1], "start") == 0) && (*nano_app)->start)
return handle_app((*nano_app)->start(argc - 2, argv + 2));
if ((strcmp(argv[1], "stop") == 0) && (*nano_app)->stop)
return handle_app((*nano_app)->stop(argc - 2, argv + 2));
if ((*nano_app)->dflt)
return handle_app((*nano_app)->dflt(argc - 1, argv + 1));
printf("Error - unknown parameter: %s\n", argv[1]);
err_param:
printf("Use one of the following parameters:\n");
if ((*nano_app)->start)
printf(" * start\n");
if ((*nano_app)->stop)
printf(" * stop\n");
return EXIT_FAILURE;
}

103
nanomq/process.c Normal file
View File

@ -0,0 +1,103 @@
#include "include/nanomq.h"
#include "include/process.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <paths.h>
int process_is_alive(int pid)
{
if (pid < 1)
return 0;
return kill(pid, 0) == 0 ? 1 : 0;
}
int process_send_signal(int pid, int signal)
{
if (pid < 1)
return 0;
debug_msg("pid = %i, signal = %i", pid, signal);
return kill(pid, signal);
}
int pidgrp_send_signal(int pid, int signal)
{
pid_t gpid;
if (pid < 1)
return 0;
gpid = getpgid(pid);
if (gpid < 0) {
debug_msg("pid = %i: unable to retrieve gpid: %s", pid,
strerror(errno));
return 0;
}
debug_msg("pid = %i: gpid = %i, signal = %i", pid, gpid, signal);
return kill(-gpid, signal);
}
int process_daemonize(void)
{
int fd;
switch (fork()) {
case -1:
return -1;
case 0:
break;
default:
exit(EXIT_SUCCESS);
}
if (setsid() == -1)
return EXIT_FAILURE;
/* Make certain we are not a session leader, or else we might reacquire
* a controlling terminal
*/
if (fork())
exit(EXIT_SUCCESS);
chdir("/");
fd = open(_PATH_DEVNULL, O_RDWR, 0);
if (fd != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > 2)
close(fd);
}
return 0;
}
int process_create_child(int(*child_run)(void *), void *data)
{
pid_t pid;
pid = fork();
switch (pid) {
case -1:
return -1;
case 0:
exit(child_run(data));
default:
return pid;
}
}

1005
nanomq/pub_handler.c Normal file

File diff suppressed because it is too large Load Diff

266
nanomq/subscribe_handle.c Normal file
View File

@ -0,0 +1,266 @@
#include <nng.h>
#include <nanolib.h>
#include <protocol/mqtt/mqtt_parser.h>
#include <protocol/mqtt/mqtt.h>
#include "include/nanomq.h"
#include "include/subscribe_handle.h"
uint8_t decode_sub_message(nng_msg * msg, packet_subscribe * sub_pkt){
uint8_t * variable_ptr;
uint8_t * payload_ptr;
int vpos = 0; // pos in variable
int bpos = 0; // pos in payload
int len_of_varint = 0, len_of_property = 0, len_of_properties = 0;
int len_of_str, len_of_topic;
size_t remaining_len = nng_msg_remaining_len(msg);
bool version_v5 = false; // v3.1.1/v5
uint8_t property_id;
topic_node * topic_node_t, * _topic_node;
// handle variable header
variable_ptr = nng_msg_variable_ptr(msg);
NNI_GET16(variable_ptr + vpos, sub_pkt->packet_id);
vpos += 2;
// Only Mqtt_v5 include property.
if(version_v5){
// length of property in varibale
len_of_properties = get_var_integer(variable_ptr+vpos, &len_of_varint);
vpos += len_of_varint;
// parse property in variable
if(len_of_properties > 0){
while(1){
property_id = variable_ptr[vpos++];
switch(property_id){
case SUBSCRIPTION_IDENTIFIER:
sub_pkt->sub_id.varint = get_var_integer(variable_ptr+vpos, &len_of_varint);
vpos += len_of_varint;
break;
case USER_PROPERTY:
// key
len_of_str = get_utf8_str(&(sub_pkt->user_property.strpair.str_key), variable_ptr, &vpos);
sub_pkt->user_property.strpair.len_key = len_of_str;
// vpos += len_of_str;
// value
len_of_str = get_utf8_str(&(sub_pkt->user_property.strpair.str_value), variable_ptr, &vpos);
sub_pkt->user_property.strpair.len_value = len_of_str;
// vpos += len_of_str;
break;
default:
// avoid error
if(vpos > remaining_len){
debug_msg("ERROR_IN_LEN_VPOS");
}
}
}
}
}
debug_msg("Remain_len: [%ld] packet_id : [%d]", remaining_len, sub_pkt->packet_id);
// handle payload
payload_ptr = nng_msg_payload_ptr(msg);
debug_msg("V:[%x %x %x %x] P:[%x %x %x %x].", variable_ptr[0], variable_ptr[1], variable_ptr[2], variable_ptr[3],
payload_ptr[0], payload_ptr[1], payload_ptr[2], payload_ptr[3]);
topic_node_t = nng_alloc(sizeof(topic_node));
topic_node_t->next = NULL;
sub_pkt->node = topic_node_t;
while(1){
topic_with_option * topic_option = nng_alloc(sizeof(topic_with_option));
topic_node_t->it = topic_option;
_topic_node = topic_node_t;
len_of_topic = get_utf8_str(&(topic_option->topic_filter.str_body), payload_ptr, &bpos); // len of topic filter
if(len_of_topic != -1){
topic_option->topic_filter.len = len_of_topic;
}else {
debug_msg("NOT utf-8 format string. ");
return PROTOCOL_ERROR;
}
debug_msg("Length of topic: %d topic_node: %x %x. ", len_of_topic, (uint8_t)(topic_option->topic_filter.str_body[0]), (uint8_t)(topic_option->topic_filter.str_body[1]));
memcpy(topic_option, payload_ptr+bpos, 1);
debug_msg("Bpos+Vpos: [%d] Remain_len:%ld.", bpos+vpos, remaining_len);
if(++bpos < remaining_len - vpos){
topic_node_t = nng_alloc(sizeof(topic_node));
topic_node_t->next = NULL;
_topic_node->next = topic_node_t;
}else{
break;
}
}
return SUCCESS;
}
uint8_t encode_suback_message(nng_msg * msg, packet_subscribe * sub_pkt){
bool version_v5 = false;
nng_msg_clear(msg);
uint8_t packet_id[2];
uint8_t reason_code, cmd;
uint32_t remaining_len;
uint8_t varint[4];
int len_of_varint;
topic_node * node;
// handle variable header first
NNI_PUT16(packet_id, sub_pkt->packet_id);
if(nng_msg_append(msg, packet_id, 2) != 0){
debug_msg("NNG_MSG_APPEND_ERROR");
return PROTOCOL_ERROR;
}
if(version_v5){ // add property in variable
}
// handle payload
node = sub_pkt->node;
while(node){
if(version_v5){
}else{
if(node->it->reason_code == 0x80){
reason_code = 0x80;
}else{
reason_code = node->it->qos;
}
// MQTT_v3: 0x00-qos0 0x01-qos1 0x02-qos2 0x80-fail
if(nng_msg_append(msg, (uint8_t *) &reason_code, 1) != 0){
debug_msg("NNG_MSG_APPEND_ERROR");
return PROTOCOL_ERROR;
}
}
node = node->next;
debug_msg("reason_code: [%x]", reason_code);
}
// handle fixed header
cmd = CMD_SUBACK;
if(nng_msg_header_append(msg, (uint8_t *) &cmd, 1) != 0){
debug_msg("NNG_HEADER_APPEND_ERROR");
return PROTOCOL_ERROR;
}
remaining_len = (uint32_t)nng_msg_len(msg);
len_of_varint = put_var_integer(varint, remaining_len);
if(nng_msg_header_append(msg, varint, len_of_varint) != 0){
debug_msg("NNG_MSG_APPEND_ERROR");
return PROTOCOL_ERROR;
}
debug_msg("remain: [%d] varint: [%d %d %d %d] len: [%d] packet_id: [%x %x]", remaining_len, varint[0], varint[1], varint[2], varint[3], len_of_varint, packet_id[0], packet_id[1]);
return SUCCESS;
}
uint8_t sub_ctx_handle(emq_work * work){
// generate ctx for each topic
int count = 0;
bool version_v5 = false;
topic_node * topic_node_t = work->sub_pkt->node;
char * topic_str;
// insert ctx_sub into treeDB
while(topic_node_t){
struct topic_and_node *tan = nng_alloc(sizeof(struct topic_and_node));
struct client * client = nng_alloc(sizeof(struct client));
//setting client
client->id = (char *)conn_param_get_clentid((conn_param *)nng_msg_get_conn_param(work->msg));
client->ctxt = work;
client->next = NULL;
topic_str = (char *)nng_alloc(topic_node_t->it->topic_filter.len + 1);
strncpy(topic_str, topic_node_t->it->topic_filter.str_body, topic_node_t->it->topic_filter.len);
topic_str[topic_node_t->it->topic_filter.len] = '\0';
debug_msg("topicLen: [%d] Body: [%s]", topic_node_t->it->topic_filter.len, (char *)topic_str);
char ** topic_queue = topic_parse(topic_str);
// debug_msg("topic_queue: -%s -%s -%s -%s", *topic_queue, *(topic_queue+1), *(topic_queue+2), *(topic_queue+3));
search_node(work->db, topic_queue, tan);
if(tan->topic){ // not contain the node
add_node(tan, client);
add_topic(client->id, topic_str);
add_pipe_id(work->pid.id, client->id);
struct topic_queue * q = get_topic(client->id);
debug_msg("-----CHECKHASHTABLE----clientid:%s---topic:%s---pipeid:%d",
client->id, q->topic, work->pid.id);
}else{
// not contain clientid
if(tan->node->sub_client==NULL || check_client(tan->node, client->id)){
add_topic(client->id, topic_str);
add_pipe_id(work->pid.id, client->id);
struct topic_queue * q = get_topic(client->id);
// debug_msg("------CHECKHASHTABLE----clientid:%s---next-topic:%s",
// client->id, q->next->topic);
add_client(tan, client);
// test
search_node(work->db, topic_queue, tan);
struct client * cli = tan->node->sub_client;
while(cli){
debug_msg("client: %s", cli->id);
cli = cli->next;
}
}else{ // clientid already in hash
work->sub_pkt->node->it->reason_code = 0x80;
}
}
nng_free(tan, sizeof(struct topic_and_node));
nng_free(topic_str, topic_node_t->it->topic_filter.len+1);
topic_node_t = topic_node_t->next;
}
// check treeDB
print_db_tree(work->db);
debug_msg("End of sub ctx handle. \n");
return SUCCESS;
}
void destroy_sub_ctx(void * ctxt, char * target_topic){
emq_work * work = ctxt;
if(!work){
return;
}
if(!work->sub_pkt){
debug_msg("ERROR : ctx->sub is nil");
return;
}
packet_subscribe * sub_pkt = work->sub_pkt;
if(!(sub_pkt->node->it)){
debug_msg("NOT FIND TOPIC");
return;
}
topic_node * topic_node_t = sub_pkt->node;
topic_node * before_topic_node = NULL;
while(topic_node_t){
if(!strncmp(topic_node_t->it->topic_filter.str_body, target_topic,
topic_node_t->it->topic_filter.len)){
debug_msg("FREE in topic_node [%s] in tree", topic_node_t->it->topic_filter.str_body);
if(before_topic_node){
before_topic_node->next = topic_node_t->next;
}else{
sub_pkt->node = topic_node_t->next;
}
nng_free(topic_node_t->it, sizeof(topic_with_option));
nng_free(topic_node_t, sizeof(topic_node));
break;
}
before_topic_node = topic_node_t;
topic_node_t = topic_node_t->next;
}
if(sub_pkt->node == NULL){
nng_free(sub_pkt, sizeof(packet_subscribe));
work->sub_pkt = NULL;
}
}

199
nanomq/unsubscribe_handle.c Normal file
View File

@ -0,0 +1,199 @@
#include <nng.h>
#include <nanolib.h>
#include <protocol/mqtt/mqtt_parser.h>
#include <protocol/mqtt/mqtt.h>
#include "include/nanomq.h"
#include "include/subscribe_handle.h"
#include "include/unsubscribe_handle.h"
uint8_t decode_unsub_message(nng_msg * msg, packet_unsubscribe * unsub_pkt){
uint8_t * variable_ptr;
uint8_t * payload_ptr;
int vpos = 0; // pos in variable
int bpos = 0; // pos in payload
int len_of_varint = 0, len_of_property = 0, len_of_properties = 0;
int len_of_str, len_of_topic;
size_t remaining_len = nng_msg_remaining_len(msg);
bool version_v5 = false; // v3.1.1/v5
uint8_t property_id;
topic_node * topic_node_t, * _topic_node;
// handle varibale header
variable_ptr = nng_msg_variable_ptr(msg);
NNI_GET16(variable_ptr, unsub_pkt->packet_id);
vpos += 2;
// Mqtt_v5 include property
if(version_v5){
// length of property in variable
len_of_properties = get_var_integer(variable_ptr, &len_of_varint);
vpos += len_of_varint;
if(len_of_properties > 0){
while(1){
property_id = variable_ptr[vpos];
switch(property_id){
case USER_PROPERTY:
// key
len_of_str = get_utf8_str(&(unsub_pkt->user_property.strpair.str_key), variable_ptr, &vpos);
unsub_pkt->user_property.strpair.len_key = len_of_str;
// value
len_of_str = get_utf8_str(&(unsub_pkt->user_property.strpair.str_value), variable_ptr, &vpos);
unsub_pkt->user_property.strpair.len_value = len_of_str;
default:
if(vpos > remaining_len){
debug_msg("ERROR_IN_LEN_VPOS");
}
}
}
}
}
debug_msg("Remain_len: [%ld] packet_id : [%d]", remaining_len, unsub_pkt->packet_id);
// handle payload
payload_ptr = nng_msg_payload_ptr(msg);
debug_msg("V:[%x %x %x %x] P:[%x %x %x %x].", variable_ptr[0], variable_ptr[1], variable_ptr[2], variable_ptr[3],
payload_ptr[0], payload_ptr[1], payload_ptr[2], payload_ptr[3]);
topic_node_t = nng_alloc(sizeof(topic_node));
unsub_pkt->node = topic_node_t;
topic_node_t->next = NULL;
while(1){
topic_with_option * topic_option = nng_alloc(sizeof(topic_with_option));
topic_node_t->it = topic_option;
_topic_node = topic_node_t;
len_of_topic = get_utf8_str(&(topic_option->topic_filter.str_body), payload_ptr, &bpos); // len of topic filter
if(len_of_topic != -1){
topic_option->topic_filter.len = len_of_topic;
}else {
debug_msg("NOT utf-8 format string.");
return PROTOCOL_ERROR;
}
debug_msg("Topiclen: [%d]", len_of_topic);
debug_msg("Bpos+Vpos: [%d] Remain_len:%ld.", bpos+vpos, remaining_len);
if(bpos < remaining_len - vpos){
topic_node_t = nng_alloc(sizeof(topic_node));
topic_node_t->next = NULL;
_topic_node->next = topic_node_t;
}else{
break;
}
}
return SUCCESS;
}
uint8_t encode_unsuback_message(nng_msg * msg, packet_unsubscribe * unsub_pkt){
bool version_v5 = false;
nng_msg_clear(msg);
uint8_t packet_id[2];
uint8_t reason_code, cmd;
uint32_t remaining_len;
uint8_t varint[4];
int len_of_varint;
topic_node * node;
// handle variable header first
NNI_PUT16(packet_id, unsub_pkt->packet_id);
if(nng_msg_append(msg, packet_id, 2) != 0){
debug_msg("NNG_MSG_APPEND_ERROR");
return PROTOCOL_ERROR;
}
if(version_v5){ // add property in variable
}
// handle payload
// no payload in mqtt_v3
if(version_v5){
node = unsub_pkt->node;
while(node){
reason_code = node->it->reason_code;
nng_msg_append(msg, (uint8_t *) &reason_code, 1);
node = node->next;
debug_msg("reason_code: [%x]", reason_code);
}
}
// handle fixed header
cmd = CMD_UNSUBACK;
if(nng_msg_header_append(msg, (uint8_t *) &cmd, 1) != 0){
debug_msg("NNG_HEADER_APPEND_ERROR");
return PROTOCOL_ERROR;
}
remaining_len = (uint32_t)nng_msg_len(msg);
len_of_varint = put_var_integer(varint, remaining_len);
if(nng_msg_header_append(msg, varint, len_of_varint) != 0){
debug_msg("NNG_MSG_APPEND_ERROR");
return PROTOCOL_ERROR;
}
debug_msg("remain: [%d] varint: [%d %d %d %d] len: [%d] packet_id: [%x %x]", remaining_len, varint[0], varint[1], varint[2], varint[3], len_of_varint, packet_id[0], packet_id[1]);
return SUCCESS;
}
uint8_t unsub_ctx_handle(emq_work * work){
bool version_v5 = false;
topic_node * topic_node_t = work->unsub_pkt->node;
char * topic_str;
char * clientid;
struct client * cli = NULL;
// delete ctx_unsub in treeDB
while(topic_node_t){
struct topic_and_node *tan = nng_alloc(sizeof(struct topic_and_node));
clientid = (char *)conn_param_get_clentid((conn_param *)nng_msg_get_conn_param(work->msg));
// parse topic string
topic_str = (char *)nng_alloc(topic_node_t->it->topic_filter.len + 1);
strncpy(topic_str, topic_node_t->it->topic_filter.str_body, topic_node_t->it->topic_filter.len);
topic_str[topic_node_t->it->topic_filter.len] = '\0';
debug_msg("finding client [%s] in topic [%s].", clientid, topic_str);
char ** topic_queue = topic_parse(topic_str);
search_node(work->db, topic_queue, tan);
if(tan->topic == NULL){ // find the topic
cli = del_client(tan, clientid);
if(cli != NULL){
// FREE clientinfo in dbtree and hashtable
destroy_sub_ctx(cli->ctxt, topic_str);
del_topic_one(clientid, topic_str);
nng_free(cli, sizeof(struct client));
debug_msg("INHASH: clientid [%s] exist?: [%d]", clientid, (int)check_id(clientid));
}
del_node(tan->node);
topic_node_t->it->reason_code = 0x00;
debug_msg("find and delete this client.");
}else{ // not find the topic
topic_node_t->it->reason_code = 0x11;
debug_msg("not find and response ack.");
}
// free local varibale
nng_free(topic_str, topic_node_t->it->topic_filter.len+1);
nng_free(tan, sizeof(struct topic_and_node));
topic_node_t = topic_node_t->next;
}
// check treeDB
// print_db_tree(work->db);
debug_msg("End of unsub ctx handle.\n");
return SUCCESS;
}

497
nng/CMakeLists.txt Normal file
View File

@ -0,0 +1,497 @@
#
# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright (c) 2012 Martin Sustrik All rights reserved.
# Copyright (c) 2013 GoPivotal, Inc. All rights reserved.
# Copyright (c) 2015-2016 Jack R. Dunaway. All rights reserved.
# Copyright 2016 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com>
# Copyright 2018 Capitar IT Group BV <info@capitar.com>
#
# 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.
#
cmake_minimum_required(VERSION 3.13)
project(nng C)
include(CheckFunctionExists)
include(CheckSymbolExists)
include(CheckStructHasMember)
include(CheckLibraryExists)
include(CheckCSourceCompiles)
include(CheckCCompilerFlag)
include(CMakeDependentOption)
include(GNUInstallDirs)
include(TestBigEndian)
include(FindUnixCommands)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
if (POLICY CMP0042)
# Newer cmake on MacOS should use @rpath
cmake_policy(SET CMP0042 NEW)
endif ()
if (POLICY CMP0079)
cmake_policy(SET CMP0079 NEW)
endif ()
if (POLICY CMP0028)
# Double colon targets are only alias or imports.
cmake_policy(SET CMP0028 NEW)
endif ()
set(CMAKE_C_STANDARD 99)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir)
if ("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif ("${isSystemDir}" STREQUAL "-1")
set(NNG_DESCRIPTION "High-Performance Scalability Protocols NextGen")
set(ISSUE_REPORT_MSG "Please consider opening an issue at https://github.com/nanomsg/nng")
# Determine library versions.
file(READ "include/nng/nng.h" nng_ver_h)
string(REGEX MATCH "NNG_MAJOR_VERSION ([0-9]*)" _ ${nng_ver_h})
set(NNG_MAJOR_VERSION ${CMAKE_MATCH_1})
string(REGEX MATCH "NNG_MINOR_VERSION ([0-9]*)" _ ${nng_ver_h})
set(NNG_MINOR_VERSION ${CMAKE_MATCH_1})
string(REGEX MATCH "NNG_PATCH_VERSION ([0-9]*)" _ ${nng_ver_h})
set(NNG_PATCH_VERSION ${CMAKE_MATCH_1})
string(REGEX MATCH "NNG_RELEASE_SUFFIX \"([a-z0-9]*)\"" _ ${nng_ver_h})
if (NOT ("${CMAKE_MATCH_1}" STREQUAL ""))
set(NNG_PRERELEASE "-${CMAKE_MATCH_1}")
endif ()
set(NNG_ABI_SOVERSION 1)
set(NNG_ABI_VERSION "${NNG_MAJOR_VERSION}.${NNG_MINOR_VERSION}.${NNG_PATCH_VERSION}${NNG_PRERELEASE}")
set(NNG_PACKAGE_VERSION "${NNG_ABI_VERSION}")
message("Configuring for NNG version ${NNG_ABI_VERSION}")
# User-defined options.
option(BUILD_SHARED_LIBS "Build shared library" ${BUILD_SHARED_LIBS})
if (CMAKE_CROSSCOMPILING)
set(NNG_NATIVE_BUILD OFF)
else ()
set(NNG_NATIVE_BUILD ON)
endif ()
# We only build command line tools and tests if we are not in a
# cross-compile situation. Cross-compiling users who still want to
# build these must enable them explicitly. Some of these switches
# must be enabled rather early as we use their values later.
#option(NNG_TESTS "Build and run tests" ${NNG_NATIVE_BUILD})
#option(NNG_TOOLS "Build extra tools" ${NNG_NATIVE_BUILD})
#option(NNG_ENABLE_NNGCAT "Enable building nngcat utility." ${NNG_TOOLS})
option(NNG_ENABLE_COVERAGE "Enable coverage reporting." OFF)
# Enable access to private APIs for our own use.
add_definitions(-DNNG_PRIVATE)
# We can use rlimit to configure the stack size for systems
# that have too small defaults. This is not used for Windows,
# which can grow thread stacks sensibly. (Note that NNG can get
# by with a smallish stack, but application callbacks might require
# larger values if using aio completion callbacks. TLS libraries may
# require larger stacks however.)
if (NOT WIN32)
option(NNG_SETSTACKSIZE "Use rlimit for thread stack size" OFF)
if (NNG_SETSTACKSIZE)
add_definitions(-DNNG_SETSTACKSIZE)
endif ()
mark_as_advanced(NNG_SETSTACKSIZE)
endif ()
option(NNG_ENABLE_STATS "Enable statistics" ON)
if (NNG_ENABLE_STATS)
add_definitions(-DNNG_ENABLE_STATS)
endif ()
mark_as_advanced(NNG_ENABLE_STATS)
if (NNG_RESOLV_CONCURRENCY)
add_definitions(-DNNG_RESOLV_CONCURRENCY=${NNG_RESOLV_CONCURRENCY})
endif ()
mark_as_advanced(NNG_RESOLV_CONCURRENCY)
if (NNG_NUM_TASKQ_THREADS)
add_definitions(-DNNG_NUM_TASKQ_THREADS=${NNG_NUM_TASKQ_THREADS})
endif ()
mark_as_advanced(NNG_NUM_TASKQ_THREADS)
set(NNG_MAX_TASKQ_THREADS 16 CACHE STRING "Upper bound on taskq threads, 0 for no limit")
mark_as_advanced(NNG_MAX_TASKQ_THREADS)
if (NNG_MAX_TASKQ_THREADS)
add_definitions(-DNNG_MAX_TASKQ_THREADS=${NNG_MAX_TASKQ_THREADS})
endif ()
# Platform checks.
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(NNG_WARN_FLAGS "-Wall -Wextra -fno-omit-frame-pointer")
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
set(NNG_WARN_FLAGS "-Wall -Wextra -fno-omit-frame-pointer")
endif ()
include(CheckSanitizer)
CheckSanitizer()
if (NOT NNG_SANITIZER STREQUAL "none")
set(NNG_SANITIZER_FLAGS "-fsanitize=${NNG_SANITIZER}")
endif ()
if (NNG_ENABLE_COVERAGE)
# NB: This only works for GCC and Clang 3.0 and newer. If your stuff
# is older than that, you will need to find something newer. For
# correct reporting, we always turn off all optimizations.
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(NNG_COVERAGE_C_FLAGS "-g -O0 --coverage")
set(CMAKE_SHARED_LINKER_FLAGS --coverage)
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
set(NNG_COVERAGE_C_FLAGS "-g -O0 --coverage")
set(CMAKE_SHARED_LINKER_FLAGS --coverage)
else ()
message(FATAL_ERROR "Unable to enable coverage for your compiler.")
endif ()
endif ()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NNG_WARN_FLAGS} ${NNG_COVERAGE_C_FLAGS} ${NNG_SANITIZER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NNG_WARN_FLAGS} ${NNG_COVERAGE_C_FLAGS} ${NNG_SANITIZER_FLAGS}")
TEST_BIG_ENDIAN(NNG_BIG_ENDIAN)
if (NNG_BIG_ENDIAN)
add_definitions(-DNNG_BIG_ENDIAN)
else ()
add_definitions(-DNNG_LITTLE_ENDIAN)
endif ()
# If the compiler is not on Windows, does it support hiding the
# symbols by default? For shared libraries we would like to do this.
if (NOT WIN32 AND NOT CYGWIN)
check_c_compiler_flag(-fvisibility=hidden NNG_HIDDEN_VISIBILITY)
if (NNG_HIDDEN_VISIBILITY)
add_definitions(-DNNG_HIDDEN_VISIBILITY)
endif ()
endif ()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_LINUX)
add_definitions(-DNNG_USE_EVENTFD)
# Windows subsystem for Linux -- smells like Linux, but it has
# some differences (SO_REUSEADDR for one).
if (CMAKE_SYSTEM_VERSION MATCHES "Microsoft")
add_definitions(-DNNG_PLATFORM_WSL)
endif ()
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "Android")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_LINUX)
add_definitions(-DNNG_PLATFORM_ANDROID)
add_definitions(-DNNG_USE_EVENTFD)
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_DARWIN)
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_FREEBSD)
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_NETBSD)
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_OPENBSD)
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "SunOS")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-DNNG_PLATFORM_SUNOS)
set(NNG_PLATFORM_POSIX ON)
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
add_definitions(-DNNG_PLATFORM_WINDOWS)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_CRT_RAND_S)
set(NNG_PLATFORM_WINDOWS ON)
# Target Windows Vista and later
add_definitions(-D_WIN32_WINNT=0x0600)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=0x0600)
elseif (CMAKE_SYSTEM_NAME MATCHES "QNX")
add_definitions(-DNNG_PLATFORM_POSIX)
add_definitions(-D__EXT_BSD)
add_definitions(-D_QNX_SOURCE)
add_definitions(-DNNG_PLATFORM_QNX)
set(NNG_PLATFORM_POSIX ON)
else ()
message(AUTHOR_WARNING "WARNING: This platform may not be supported: ${CMAKE_SYSTEM_NAME}")
message(AUTHOR_WARNING "${ISSUE_REPORT_MSG}")
# blithely hope for POSIX to work
add_definitions(-DNNG_PLATFORM_POSIX)
endif ()
macro(nng_check_func SYM DEF)
check_function_exists(${SYM} ${DEF})
if (${DEF})
add_definitions(-D${DEF}=1)
endif ()
endmacro(nng_check_func)
macro(nng_check_sym SYM HDR DEF)
check_symbol_exists(${SYM} ${HDR} ${DEF})
if (${DEF})
add_definitions(-D${DEF}=1)
endif ()
endmacro(nng_check_sym)
macro(nng_check_lib LIB SYM DEF)
check_library_exists(${LIB} ${SYM} "" ${DEF})
if (${DEF})
add_definitions(-D${DEF}=1)
list(APPEND NNG_LIBS ${LIB})
endif ()
endmacro(nng_check_lib)
macro(nng_check_struct_member STR MEM HDR DEF)
check_struct_has_member("struct ${STR}" ${MEM} ${HDR} ${DEF})
if (${DEF})
add_definitions(-D${DEF}=1)
endif ()
endmacro(nng_check_struct_member)
if (WIN32)
# Windows is a special snowflake.
list(APPEND NNG_LIBS ws2_32 mswsock advapi32)
nng_check_sym(InitializeConditionVariable windows.h NNG_HAVE_CONDVAR)
nng_check_sym(snprintf stdio.h NNG_HAVE_SNPRINTF)
if (NOT NNG_HAVE_CONDVAR OR NOT NNG_HAVE_SNPRINTF)
message(FATAL_ERROR
"Modern Windows API support is missing. "
"Versions of Windows prior to Vista are not supported. "
"Further, the 32-bit MinGW environment is not supported. "
"Ensure you have at least Windows Vista or newer, and are "
"using either Visual Studio 2013 or newer or MinGW-W64.")
endif ()
else ()
# Unconditionally declare the following feature test macros. These are
# needed for some platforms (glibc and SunOS/illumos) and are harmless
# on the others.
add_definitions(-D_GNU_SOURCE)
add_definitions(-D_REENTRANT)
add_definitions(-D_THREAD_SAFE)
add_definitions(-D_POSIX_PTHREAD_SEMANTICS)
list(APPEND NNG_PKGS Threads)
find_package(Threads REQUIRED)
nng_check_func(lockf NNG_HAVE_LOCKF)
nng_check_func(flock NNG_HAVE_FLOCK)
nng_check_func(getrandom NNG_HAVE_GETRANDOM)
nng_check_func(arc4random_buf NNG_HAVE_ARC4RANDOM)
nng_check_lib(rt clock_gettime NNG_HAVE_CLOCK_GETTIME)
nng_check_lib(pthread sem_wait NNG_HAVE_SEMAPHORE_PTHREAD)
nng_check_lib(pthread pthread_atfork NNG_HAVE_PTHREAD_ATFORK_PTHREAD)
nng_check_lib(nsl gethostbyname NNG_HAVE_LIBNSL)
nng_check_lib(socket socket NNG_HAVE_LIBSOCKET)
# GCC needs libatomic on some architectures (e.g. ARM) because the
# underlying architecture may lack the necessary atomic primitives.
# One hopes that the libatomic implementation is superior to just using
# a pthread mutex. The symbol chosen here was identified from GCC's
# libatomic map file.
#
# Arguably when using clang, compiler-rt might be better.
nng_check_lib(atomic __atomic_load_1 NNG_HAVE_LIBATOMIC)
nng_check_sym(AF_UNIX sys/socket.h NNG_HAVE_UNIX_SOCKETS)
nng_check_sym(backtrace_symbols_fd execinfo.h NNG_HAVE_BACKTRACE)
nng_check_struct_member(msghdr msg_control sys/socket.h NNG_HAVE_MSG_CONTROL)
nng_check_sym(eventfd sys/eventfd.h NNG_HAVE_EVENTFD)
nng_check_sym(kqueue sys/event.h NNG_HAVE_KQUEUE)
nng_check_sym(port_create port.h NNG_HAVE_PORT_CREATE)
nng_check_sym(epoll_create sys/epoll.h NNG_HAVE_EPOLL)
nng_check_sym(epoll_create1 sys/epoll.h NNG_HAVE_EPOLL_CREATE1)
nng_check_sym(getpeereid unistd.h NNG_HAVE_GETPEEREID)
nng_check_sym(SO_PEERCRED sys/socket.h NNG_HAVE_SOPEERCRED)
nng_check_struct_member(sockpeercred uid sys/socket.h NNG_HAVE_SOCKPEERCRED)
nng_check_sym(LOCAL_PEERCRED sys/un.h NNG_HAVE_LOCALPEERCRED)
nng_check_sym(getpeerucred ucred.h NNG_HAVE_GETPEERUCRED)
nng_check_sym(atomic_flag_test_and_set stdatomic.h NNG_HAVE_STDATOMIC)
endif ()
nng_check_sym(strlcpy string.h NNG_HAVE_STRLCPY)
nng_check_sym(strnlen string.h NNG_HAVE_STRNLEN)
nng_check_sym(strcasecmp string.h NNG_HAVE_STRCASECMP)
nng_check_sym(strncasecmp string.h NNG_HAVE_STRNCASECMP)
# Set a static symbol. We do this for testing, so that tests can
# be skipped if they would rely on symbols that might not be exported.
# For example, idhash depends on private symbols, so don't test it
# when using a shared library on Windows because the symbols won't
# resolve.
if (NOT (BUILD_SHARED_LIBS))
set(NNG_STATIC_LIB ON)
message(STATUS "Building static libs")
endif ()
# In order to facilitate testing, we want to add a library that includes
# our common test code. We do this before iterating everywhere else so
# that we can locate our tests inside the directories where we want.
if (NNG_TESTS)
enable_testing()
set(all_tests, "")
endif ()
macro(nng_test NAME)
if (NNG_TESTS)
add_executable(${NAME} ${NAME}.c ${ARGN})
target_link_libraries(${NAME} ${PROJECT_NAME}_testlib)
target_include_directories(${NAME} PRIVATE
${PROJECT_SOURCE_DIR}/tests
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/include)
add_test(NAME ${NAME} COMMAND ${NAME} -t)
set_tests_properties(${NAME} PROPERTIES TIMEOUT 180)
endif ()
endmacro()
function(nng_sources_testlib)
if (NNG_TESTS)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME}_testlib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f})
endforeach ()
endif ()
endfunction()
function(nng_headers_testlib)
if (NNG_TESTS)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME}_testlib PRIVATE ${PROJECT_SOURCE_DIR}/include/${f})
endforeach ()
endif ()
endfunction()
function(nng_defines_testlib)
if (NNG_TESTS)
target_compile_definitions(${PROJECT_NAME}_testlib PRIVATE ${ARGN})
endif ()
endfunction()
function(nng_sources)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f})
endforeach ()
nng_sources_testlib(${ARGN})
endfunction()
function(nng_headers)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include/${f})
endforeach ()
nng_headers_testlib(${ARGN})
endfunction()
function(nng_defines)
target_compile_definitions(${PROJECT_NAME} PRIVATE ${ARGN})
nng_defines_testlib(${ARGN})
endfunction()
# nng_sources_if adds the sources unconditionally to the test library,
# but conditionally to the production library. This allows us to get
# full test coverage while allowing a minimized delivery.
function(nng_sources_if COND)
if (${COND})
foreach (f ${ARGN})
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f})
endforeach ()
endif ()
nng_sources_testlib(${ARGN})
endfunction()
function(nng_headers_if COND)
if (COND)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include/${f})
endforeach ()
endif ()
nng_headers_testlib(${ARGN})
endfunction()
function(nng_defines_if COND)
if (${COND})
# Revisit this one
target_compile_definitions(${PROJECT_NAME} PUBLIC ${ARGN})
endif ()
nng_defines_testlib(${ARGN})
endfunction()
add_subdirectory(src)
foreach (_PKG IN ITEMS ${NNG_PKGS})
find_package(${_PKG} REQUIRED)
endforeach ()
add_definitions(${NNG_DEFS})
if (NNG_TESTS)
#add_subdirectory(tests)
#add_subdirectory(perf)
endif ()
# Build the tools
if (NNG_ENABLE_NNGCAT)
#add_subdirectory(tools/nngcat)
endif ()
option(NNG_ENABLE_TLS "Enable TLS protocol" OFF)
if (NNG_ENABLE_TLS)
add_definitions(-DNNG_SUPP_TLS)
set(NNG_SUPP_TLS ON)
endif ()
#add_subdirectory(docs/man)
# for deleting node when client closed abnormally
include_directories(${CMAKE_SOURCE_DIR}/nanolib/include)
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION ${NNG_PACKAGE_VERSION})
set(CPACK_PACKAGE_CONTACT "nanomsg@freelists.org")
set(CPACK_PACKAGE_VENDOR "nanomsg.org")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "nanomsg next generation library")
set(CPACK_SOURCE_GENERATOR "TBZ2;TGZ;ZIP")
set(CPACK_SOURCE_IGNORE_FILES "/build/;/.git/;~$;${CPACK_SOURCE_IGNORE_FILES}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME
"${PROJECT_NAME}-v${NNG_PACKAGE_VERSION}-src")
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "nng")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-v${NNG_PACKAGE_VERSION}")
add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
include(CPack)

201
nng/LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

22
nng/LICENSE.txt Normal file
View File

@ -0,0 +1,22 @@
The MIT License
Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
Copyright 2018 Capitar IT Group BV <info@capitar.com>
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.

66
nng/Makefile Normal file
View File

@ -0,0 +1,66 @@
include config.mk
export EMQ_DEBUG=
PKG_NAME:=NanoMQ
PKG_VERSION:=0.1
#PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:=
PKG_MD5SUM:=
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/NanoMQ
SECTION:=utils
CATEGORY:=Utilities
TITLE:=blazing fast + ultra lighweight MQTT edge broker
#URL:=
DEPENDS:=+libnl-tiny +librt
endef
define Package/NanoMQ/description
package to communicate with a dashboard
endef
define Package/NanoMQ/config
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/ $(PKG_BUILD_DIR)/src
endef
define Build/Configure
endef
define Package/nng/postinst
#!/bin/sh
endef
TARGET_CFLAGS:= \
-I$(STAGING_DIR)/usr/include/libnl-tiny \
-I$(STAGING_DIR)/usr/include/ \
$(TARGET_CFLAGS)
TARGET_LDFLAGS:= \
-lnl-tiny \
$(TARGET_LDFLAGS)
MAKE_FLAGS += \
OFLAGS="$(TARGET_CFLAGS) $(TARGET_LDFLAGS)" \
CC="$(TARGET_CC)" \
STRIP="/bin/true"
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR)/src $(MAKE_FLAGS)
endef
define Package/NanoMQ/install
endef
$(eval $(call BuildPackage,dashboard))

View File

@ -0,0 +1,30 @@
#
# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2017 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
#
macro (CheckSanitizer)
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(NNG_SAN_LIST none address leak memory thread undefined)
elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(NNG_SAN_LIST none address leak memory thread undefined)
elseif (CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
set(NNG_SAN_LIST none address thread undefined)
else ()
set(NNG_SAN_LIST none)
endif ()
set (NNG_SANITIZER none CACHE STRING "Sanitizer to use (clang or gcc).")
set_property(CACHE NNG_SANITIZER PROPERTY STRINGS ${NNG_SAN_LIST})
mark_as_advanced (NNG_SANITIZER)
if (NOT NNG_SANITIZER STREQUAL "none")
set (NNG_C_FLAG_SANITIZER "-fsanitize=${NNG_SANITIZER}")
message(STATUS "Enabling sanitizer: ${NNG_C_FLAG_SANITIZER}")
endif()
endmacro ()

View File

@ -0,0 +1,80 @@
#
# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2017 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
#
#
# Try to find the Mbed TLS libraries.
#
# Sets the following:
#
# MBEDTLS_INCLUDE_DIR - Where to find mbedtls/ssl.h, etc.
# MBEDTLS_FOUND - True if we found Mbed TLS.
# MBEDTLS_CRYPTO_LIBRARY - The mbedcrypto library.
# MBEDTLS_X509_LIBRARY - The mbedx509 library.
# MBEDTLS_TLS_LIBRARY - The mbedtls library.
# MBEDTLS_LIBRARIES - List of all three Mbed TLS libraries.
# MBEDTLS_VERSION - $major.$minor.$revision (e.g. ``2.6.0``).
#
# Hints:
#
# Set ``MBEDTLS_ROOT_DIR`` to the root directory of Mbed TLS installation.
#
set(_MBEDTLS_ROOT_HINTS ${MBEDTLS_ROOT_DIR} ENV MBEDTLS_ROOT_DIR)
include(FindPackageHandleStandardArgs)
find_path(MBEDTLS_INCLUDE_DIR
NAMES mbedtls/ssl.h
HINTS ${_MBEDTLS_ROOT_HINTS}
PATHS /usr/local
PATH_SUFFIXES include)
find_library(MBEDTLS_CRYPTO_LIBRARY
NAMES mbedcrypto
HINTS ${_MBEDTLS_ROOT_HINTS}
PATHS /usr/local
PATH_SUFFIXES lib)
find_library(MBEDTLS_X509_LIBRARY
NAMES mbedx509
HINTS ${_MBEDTLS_ROOT_HINTS}
PATHS /usr/local
PATH_SUFFIXES lib)
find_library(MBEDTLS_TLS_LIBRARY
NAMES mbedtls
HINTS ${_MBEDTLS_ROOT_HINTS}
PATHS /usr/local
PATH_SUFFIXES lib)
set(MBEDTLS_LIBRARIES
${MBEDTLS_TLS_LIBRARY}
${MBEDTLS_X509_LIBRARY}
${MBEDTLS_CRYPTO_LIBRARY})
if (${MBEDTLS_TLS_LIBRARY-NOTFOUND})
message(FATAL_ERROR "Failed to find Mbed TLS library")
endif()
mark_as_advanced(
MBEDSSL_INCLUDE_DIR
MBEDTLS_LIBRARIES
MBEDTLS_CRYPTO_LIBRARY
MBEDTLS_X509_LIBRARY
MBEDTLS_TLS_LIBRARY)
# Extract the version from the header... hopefully it matches the library.
file(STRINGS ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h _MBEDTLS_VERLINE
REGEX "^#define[ \t]+MBEDTLS_VERSION_STRING[\t ].*")
string(REGEX REPLACE ".*MBEDTLS_VERSION_STRING[\t ]+\"(.*)\"" "\\1" MBEDTLS_VERSION ${_MBEDTLS_VERLINE})
find_package_handle_standard_args(mbedTLS
REQUIRED_VARS MBEDTLS_TLS_LIBRARY MBEDTLS_CRYPTO_LIBRARY MBEDTLS_X509_LIBRARY MBEDTLS_INCLUDE_DIR VERSION_VAR MBEDTLS_VERSION)

View File

@ -0,0 +1,26 @@
# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
@PACKAGE_INIT@
set(NNG_VERSION_MAJOR "@NNG_VERSION_MAJOR@")
set(NNG_VERSION_MINOR "@NNG_VERSION_MINOR@")
set(NNG_VERSION_PATCH "@NNG_VERSION_PATCH@")
set_and_check(NNG_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIRS@")
include("${CMAKE_CURRENT_LIST_DIR}/nng-targets.cmake")
# Make sure we find packages for our dependencies
foreach(_PKG IN ITEMS @NNG_PKGS@)
find_package(${_PKG} REQUIRED)
endforeach ()
set(NNG_LIBRARY nng::nng)
check_required_components(@PROJECT_NAME@)

74
nng/etc/README.adoc Normal file
View File

@ -0,0 +1,74 @@
About This Directory
====================
This directory contains support files that I use for developing this
project. I recommend others consider doing the same.
Coding Style
~~~~~~~~~~~~~~~~~~~~~~~~~
A clang-format configuration file is included in the project, you should
use that. (clang format requires that the style file live at the top of
of the project tree, not somewhere else, such as this etc directory.) The
format rules are probably inappropriate for the test/ subdirectory, as
the Convey framework has a style all it's own.
The style is based loosely on WebKit, but is really modified -- someday
if clang-format ever learns about BSD KNF I'll probably switch to that.
Once upon a time this used uncrustify. However, the frequent breaking changes
in uncrustify versions, and the fact that the latest (at this time 0.65) does
not actually support one of the most common language constructs (extern "C"
opening braces) has driven me towards clang-format. The good news is that
this is probably actually easier for most folks to use.
Sublime Text
~~~~~~~~~~~~
I've also arranged for Sublime text to understand that .h is C, not C++ (this
is important!)
ISO Standard C
~~~~~~~~~~~~~~
I've decided, after some gnashing of teeth, to finally accept that C99
is here to stay. Therefore, I'm *not* spending any effort into supporting
older C89/C90 compilers. That said I do understand that compiler support
for C99 is not always complete. I will stick to the mainstream features,
like <stdint.h>, the ability to use variadic macros, // comments, and perhaps
the occasional use of for() locally scoped variables.
We also insist that you have working vsnprintf, snprintf. Microsoft famously
did not, or worse, had broken ones (that didn't guarantee NULL termination).
Visual Studio 2015 reportedly fixes this. Building with older versions of
Visual Studio for Microsoft platforms may leave you with some brittle code
that could break in some bad ways -- use the latest to avoid this issue.
(I'm not aware of any other platform with this kind of brain damage.)
Naming Conventions
~~~~~~~~~~~~~~~~~~
Because not everyone wants to deal with CMake all the time, I anticipate that
there will be folks who in the future want to just create one monster .c
file that contains all these things, or even a .h that they just inline into
their programmer. As vile as this idea seems to me, I can understand the
motivations for it. In order to facilitate those cases, its important that
all global symbols use names prefixed with nni_ or nng_ (or NNI_ or NNG_ for
macro names). This is true even for static symbols that won't show up in
a more conventional symbol table.
We use nng_ (and NNG_) for symbols that are intended to be expoed to consumers.
These symbols form part of our public API.
We use nni_ and NNI_ for symbols that are *NOT* part of our public API and
should not be used by users.
Note that for the most part we try to avoid exposing structures directly to
users so that they don't get baked into binaries -- preferring instead to
dynamically allocate and give back an opaque pointer to the API. Any
exceptions to this case need to be VERY carefully reviewed to make sure
that the thing is unlikely to change (in any way whatsoever) in the future,
or that adequate provisions for versioning have been made.

20
nng/etc/codecov.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/bash
# Copyright 2017 Garrett D'Amore <garrett@damore.org>
# Copyright 2017 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
if [ "${COVERAGE}" != ON ]
then
echo "Code coverage not enabled."
exit 0
fi
GCOV=${GCOV:-gcov}
bash <(curl -s https://codecov.io/bash) -x gcov || echo "Codecov did not collect coverage"
echo 0

33
nng/etc/coverage.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
# Copyright 2017 Garrett D'Amore <garrett@damore.org>
# Copyright 2017 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
if [ "${COVERAGE}" != ON ]
then
echo "Code coverage not enabled."
exit 0
fi
GCOV=${GCOV:-gcov}
# capture all coverage info
lcov --gcov-tool ${GCOV} --directory . --capture --output-file coverage.info || exit 1
# filter out system information (C++ templates & inlines)
lcov --remove coverage.info '/usr/*' --output-file coverage.info || exit 1
# filter out the *test* program data
lcov --remove coverage.info '*/tests/*' --output-file coverage.info || exit 1
# emit debug stats.
lcov --list coverage.info
rm coverage.info
echo 0

94
nng/etc/format-check.sh Executable file
View File

@ -0,0 +1,94 @@
#!/bin/sh
#
# Copyright 2016 Garrett D'Amore <garrett@damore.org>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
# file was obtained (LICENSE.txt). A copy of the license may also be
# found online at https://opensource.org/licenses/MIT.
#
#
# This script is used to run uncrustify and report files that don't match.
# It looks for .c and .h files, located in ../src, and uses the config file
# uncrustify.cfg located in the same directory as this script. It only handles
# C language at this point.
#
CLANG_FORMAT=${CLANG_FORMAT:-clang-format}
case "${CLANG_FORMAT}" in
no|off|skip|NO|OFF|SKIP)
echo "format checks skipped"
exit 0
;;
esac
mydir=$(dirname $0)
srcdir=${mydir}/../src
failed=
vers=$(${CLANG_FORMAT} -version)
if [ $? -ne 0 ]; then
echo "clang format not found? Skipping checks."
exit 0
fi
versno=${vers#clang-format version }
prefix=${vers%${versno}}
if [ "$prefix" != "clang-format version " ]
then
echo "clang-format version misparsed. Skipping checks."
exit 0
fi
# strip off any -ubuntu suffix
versno=${versno%%-*}
maj=${versno%%.*}
rem=${versno#*.}
min=${rem%%.*}
if [ "${maj}" -lt 3 ]; then
echo "clang-format is too old. Skipping checks."
exit 0
fi
if [ "${maj}" -eq 3 -a "${min}" -lt 6 ]; then
echo "clang-format is too old. Skipping checks."
exit 0
fi
mytmpdir=$(mktemp -d)
diffprog=${DIFF:-diff}
if [ -t 1 ]; then
if colordiff -q /dev/null > /dev/null 2>&1; then
diffprog=${DIFF:-colordiff}
fi
fi
cd ${srcdir}
for file in $(find . -name '*.[ch]' -print)
do
ext=${file##*.}
oldf=${file}
newf=${mytmpdir}/new.${ext}
# If we do not understand the format file, then do nothing
# Our style requires a relatively modern clang-format, which is
# older than is found on some Linux distros.
${CLANG_FORMAT} -fallback-style=none -style=file ${oldf} > ${newf}
cmp -s ${oldf} ${newf}
if [ $? -ne 0 ]
then
echo "${file} style changes"
${diffprog} -u $oldf $newf
failed=1
fi
done
rm -rf $mytmpdir
if [ -n "$failed" ]
then
echo "Format differences found!" 1>&2
# Sadly, there are different versions of Uncrustify, and they don't
# seem to universally agree. So let's not trigger a build error on
# this -- but instead just emit it to standard output.
exit 0
fi

View File

@ -0,0 +1,17 @@
{
"folders":
[
{
"path": ".."
}
],
"settings":
{
"tab_size": 8,
"translate_tabs_to_spaces": false,
"ClangFormat": {
"style": "File",
"format_on_save": true
}
}
}

View File

@ -0,0 +1,33 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_BUS_H
#define NNG_COMPAT_BUS_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// BUS sockopt level.
#define NN_PROTO_BUS 7
#define NN_BUS (NN_PROTO_BUS * 16 + 0)
// BUS has no options.
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_BUS_H

View File

@ -0,0 +1,31 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_INPROC_H
#define NNG_COMPAT_INPROC_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// inproc sockopt level.
// There are no inproc tunables.
#define NN_INPROC (-1)
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_INPROC_H

View File

@ -0,0 +1,39 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_IPC_H
#define NNG_COMPAT_IPC_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// IPC sockopt level.
#define NN_IPC (-2)
// IPC options. Note that these are not currently supported.
// IPC_SEC_ATTR works quite differently in NNG, and must be
// configured using the new API. The buffer sizing options are
// not supported at all. None of these were ever documented, and
// are offered here only for source compatibility.
#define NN_IPC_SEC_ATTR 1
#define NN_IPC_OUTBUFSZ 2
#define NN_IPC_INBUFSZ 3
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_IPC_H

View File

@ -0,0 +1,284 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_NN_H
#define NNG_COMPAT_NN_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
// Note that compatibility promises are limited to public portions of the
// nanomsg API, and specifically do NOT extend to the ABI. Furthermore,
// there may be other limitations around less commonly used portions of the
// API; for example only SP headers may be transported in control data for
// messages, there is almost no compatibility offered for statistics.
// Error values may differ from those returned by nanomsg as well; the nng
// error reporting facility expresses only a subset of the possibilities of
// nanomsg.
// Note that unlike nanomsg, nng does not aggressively recycle socket or
// endpoint IDs, which means applications which made assumptions that these
// would be relatively small integers (e.g. to use them as array indices)
// may break. (No promise about values was ever made.)
#ifdef __cplusplus
extern "C" {
#endif
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
// clang-format gets in the way of most of this file.
// We turn it off, at least until it gets smarter about aligning
// macro definitions or we adopt enums or somesuch.
// clang-format off
// NNG_DECL is used on declarations to deal with scope.
// For building Windows DLLs, it should be the appropriate __declspec().
// For shared libraries with platforms that support hidden visibility,
// it should evaluate to __attribute__((visibility("default"))).
#ifndef NN_DECL
#if defined(_WIN32) && !defined(NNG_STATIC_LIB)
#if defined(NNG_SHARED_LIB)
#define NN_DECL __declspec(dllexport)
#else
#define NN_DECL __declspec(dllimport)
#endif // NNG_SHARED_LIB
#else
#if defined(NNG_SHARED_LIB) && defined(NNG_HIDDEN_VISIBILITY)
#define NN_DECL __attribute__((visibility("default")))
#else
#define NN_DECL extern
#endif
#endif // _WIN32 && !NNG_STATIC_LIB
#endif // NN_DECL
#define AF_SP 1
#define AF_SP_RAW 2
#define NN_SOCKADDR_MAX 128
#define NN_SOL_SOCKET 0
// Flag for send/recv (nonblocking)
#define NN_DONTWAIT 1
// CMSG data type
#define PROTO_SP 1
#define SP_HDR 1
// Errnos. Legacy nanomsg uses posix errnos where possible.
// If a define is not set, use add NN_ERRBASE. nng does not
// return all of these values, so there may be some loss of
// of information for edge cases, but we don't expect that to be
// a problem really.
#define NN_ERRBASE (0x10000000)
#ifndef ENOTSUP
#define ENOTSUP (NN_ERRBASE+1)
#endif
#ifndef EPROTONOSUPPORT
#define EPROTONOSUPPORT (NN_ERRBASE+2)
#endif
#ifndef ENOBUFS
#define ENOBUFS (NN_ERRBASE+3)
#endif
#ifndef ENETDOWN
#define ENETDOWN (NN_ERRBASE+4)
#endif
#ifndef EADDRINUSE
#define EADDRINUSE (NN_ERRBASE+5)
#endif
#ifndef EADDRNOTAVAIL
#define EADDRNOTAVAIL (NN_ERRBASE+6)
#endif
#ifndef ENOTSOCK
#define ENOTSOCK (NN_ERRBASE+7)
#endif
#ifndef EAGAIN
#define EAGAIN (NN_ERRBASE+8)
#endif
#ifndef EBADF
#define EBADF (NN_ERRBASE+9)
#endif
#ifndef EINVAL
#define EINVAL (NN_ERRBASE+10)
#endif
#ifndef EMFILE
#define EMFILE (NN_ERRBASE+11)
#endif
#ifndef EFAULT
#define EFAULT (NN_ERRBASE+12)
#endif
#ifndef EACCES
#define EACCES (NN_ERRBASE+13)
#endif
#ifndef ENETRESET
#define ENETRESET (NN_ERRBASE+14)
#endif
#ifndef ENETUNREACH
#define ENETUNREACH (NN_ERRBASE+15)
#endif
#ifndef EHOSTUNREACH
#define EHOSTUNREACH (NN_ERRBASE+16)
#endif
#ifndef EAFNOSUPPORT
#define EAFNOSUPPORT (NN_ERRBASE+17)
#endif
#ifndef EINPROGRESS
#define EINPROGRESS (NN_ERRBASE+18)
#endif
#ifndef EPROTO
#define EPROTO (NN_ERRBASE+19)
#endif
#ifndef ECONNREFUSED
#define ECONNREFUSED (NN_ERRBASE+20)
#endif
#ifndef ENOTCONN
#define ENOTCONN (NN_ERRBASE+21)
#endif
#ifndef EMSGSIZE
#define EMSGSIZE (NN_ERRBASE+22)
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT (NN_ERRBASE+23)
#endif
#ifndef ECONNABORTED
#define ECONNABORTED (NN_ERRBASE+24)
#endif
#ifndef ECONNRESET
#define ECONNRESET (NN_ERRBASE+25)
#endif
#ifndef ENOPROTOOPT
#define ENOPROTOOPT (NN_ERRBASE+26)
#endif
#ifndef EISCONN
#define EISCONN (NN_ERRBASE+27)
#endif
#ifndef ESOCKNOSUPPORT
#define ESOCKNOSPPORT (NN_ERRBASE+28)
#endif
#ifndef ETERM
#define ETERM (NN_ERRBASE+29)
#endif
#ifndef EFSM
#define EFSM (NN_ERRBASE+30)
#endif
#ifndef ENOENT
#define ENOENT (NN_ERRBASE+31)
#endif
#ifndef EIO
#define EIO (NN_ERRBASE+32)
#endif
#ifndef EEXIST
#define EEXIST (NN_ERRBASE+33)
#endif
#ifndef ENOSPC
#define ENOSPC (NN_ERRBASE+34)
#endif
// Socket options
#define NN_LINGER 1
#define NN_SNDBUF 2
#define NN_RCVBUF 3
#define NN_SNDTIMEO 4
#define NN_RCVTIMEO 5
#define NN_RECONNECT_IVL 6
#define NN_RECONNECT_IVL_MAX 7
#define NN_SNDPRIO 8
#define NN_RCVPRIO 9
#define NN_SNDFD 10
#define NN_RCVFD 11
#define NN_DOMAIN 12
#define NN_PROTOCOL 13
#define NN_IPV4ONLY 14
#define NN_SOCKET_NAME 15
#define NN_RCVMAXSIZE 16
#define NN_MAXTTL 17
// from this point on formatting is fine
// clang-format on
// Poll stuff
#define NN_POLLIN 1
#define NN_POLLOUT 2
struct nn_pollfd {
int fd;
uint16_t events;
uint16_t revents;
};
// Magical size for allocation
#define NN_MSG ((size_t) -1)
struct nn_iovec {
void * iov_base;
size_t iov_len;
};
struct nn_msghdr {
struct nn_iovec *msg_iov;
int msg_iovlen;
void * msg_control;
size_t msg_controllen;
};
struct nn_cmsghdr {
size_t cmsg_len;
int cmsg_level;
int cmsg_type;
};
#define NN_CMSG_ALIGN(len) \
(((len) + sizeof(size_t) - 1) & (size_t) ~(sizeof(size_t) - 1))
// Unlike old nanomsg, we explicitly only support the SP header as attached
// cmsg data. It turns out that old nanomsg didn't really store anything
// useful otherwise anyway. (One specific exception was that it stored the
// message type of text or binary for the websocket transport. We don't think
// anyone used that in practice though.)
#define NN_CMSG_FIRSTHDR(mh) nn_cmsg_next((struct nn_msghdr *) (mh), NULL)
#define NN_CMSG_NXTHDR(mh, ch) \
nn_cmsg_next((struct nn_msghdr *) (mh), (struct nn_cmsghdr *) ch)
#define NN_CMSG_DATA(ch) ((unsigned char *) (((struct nn_cmsghdr *) (ch)) + 1))
#define NN_CMSG_SPACE(len) \
(NN_CMSG_ALIGN(len) + NN_CMSG_ALIGN(sizeof(struct nn_cmsghdr)))
#define NN_CMSG_LEN(len) (NN_CMSG_ALIGN(sizeof(struct nn_cmsghdr)) + (len))
NN_DECL struct nn_cmsghdr *nn_cmsg_next(
struct nn_msghdr *, struct nn_cmsghdr *);
NN_DECL int nn_socket(int, int);
NN_DECL int nn_setsockopt(int, int, int, const void *, size_t);
NN_DECL int nn_getsockopt(int, int, int, void *, size_t *);
NN_DECL int nn_bind(int, const char *);
NN_DECL int nn_connect(int, const char *);
NN_DECL int nn_shutdown(int, int);
NN_DECL int nn_send(int, const void *, size_t, int);
NN_DECL int nn_recv(int, void *, size_t, int);
NN_DECL int nn_sendmsg(int, const struct nn_msghdr *, int);
NN_DECL int nn_recvmsg(int, struct nn_msghdr *, int);
NN_DECL int nn_close(int);
NN_DECL int nn_poll(struct nn_pollfd *, int, int);
NN_DECL int nn_device(int, int);
NN_DECL uint64_t nn_get_statistic(int, int);
NN_DECL void * nn_allocmsg(size_t, int);
NN_DECL void * nn_reallocmsg(void *, size_t);
NN_DECL int nn_freemsg(void *);
NN_DECL int nn_errno(void);
NN_DECL const char *nn_strerror(int);
NN_DECL void nn_term(void);
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_NN_H

View File

@ -0,0 +1,39 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_PAIR_H
#define NNG_COMPAT_PAIR_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// PAIR sockopt level.
#define NN_PROTO_PAIR 1
#define NN_PAIR (NN_PROTO_PAIR * 16 + 0)
// These are technically "new", and not available in nanomsg, but
// offered here as a transition aid. If you want to use the advanced
// PAIRv1 options (POLYAMOROUS mode) you still need to use the new API.
#define NN_PAIR_v0 (NN_PROTO_PAIR * 16 + 0)
#define NN_PAIR_V1 (NN_PROTO_PAIR * 16 + 1)
// PAIR has no options.
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_PAIR_H

View File

@ -0,0 +1,34 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_PIPELINE_H
#define NNG_COMPAT_PIPELINE_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// PUSH and PULL sockopt level.
#define NN_PROTO_PIPELINE 5
#define NN_PUSH (NN_PROTO_PIPELINE * 16 + 0)
#define NN_PULL (NN_PROTO_PIPELINE * 16 + 1)
// PUSH and PULL have no options.
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_PIPELINE_H

View File

@ -0,0 +1,36 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_PUBSUB_H
#define NNG_COMPAT_PUBSUB_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// PUB and SUB sockopt level.
#define NN_PROTO_PUBSUB 2
#define NN_PUB (NN_PROTO_PUBSUB * 16 + 0)
#define NN_SUB (NN_PROTO_PUBSUB * 16 + 1)
// SUB options. (PUB has none.)
#define NN_SUB_SUBSCRIBE (NN_SUB * 16 + 1)
#define NN_SUB_UNSUBSCRIBE (NN_SUB * 16 + 2)
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_PUBSUB_H

View File

@ -0,0 +1,35 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_REQREP_H
#define NNG_COMPAT_REQREP_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// REQ and REP sockopt level.
#define NN_PROTO_REQREP 3
#define NN_REQ (NN_PROTO_REQREP * 16 + 0)
#define NN_REP (NN_PROTO_REQREP * 16 + 1)
// REQ options. (REP has none.)
#define NN_REQ_RESEND_IVL (NN_REQ * 16 + 1)
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_REQREP_H

View File

@ -0,0 +1,36 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_SURVEY_H
#define NNG_COMPAT_SURVEY_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// SURVEYOR and RESPONDENT sockopt level.
#define NN_PROTO_SURVEY 6
#define NN_SURVEYOR (NN_PROTO_SURVEY * 16 + 2)
#define NN_RESPONDENT (NN_PROTO_SURVEY * 16 + 3)
// SURVEYOR options. (RESPONDENT has none.)
#define NN_SURVEYOR_DEADLINE (NN_SURVEYOR * 16 + 1)
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_SURVEY_H

View File

@ -0,0 +1,33 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_TCP_H
#define NNG_COMPAT_TCP_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// TCP sockopt level.
#define NN_TCP (-3)
// TCP options.
#define NN_TCP_NODELAY 1
#ifdef __cplusplus
}
#endif
#endif // NNG_COMPAT_TCP_H

View File

@ -0,0 +1,41 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef NNG_COMPAT_WS_H
#define NNG_COMPAT_WS_H
// This header contains interfaces that are intended to offer compatibility
// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
// and consumers should only use these if they are porting software that
// previously used nanomsg. New programs should use the nng native APIs.
#ifdef __cplusplus
extern "C" {
#endif
// WS sockopt level.
#define NN_WS (-4)
// WS options.
// Note that while legacy libnanomsg had *some* support for text messages,
// NNG only supports binary. Binary types are required to pass protocol
// headers with NNG and nanomsg in any event. This means that the NNG
// WebSocket support will not be compatible with some very old browsers.
#define NN_WS_MSG_TYPE 1