rebase onto develop-2.0
This commit is contained in:
@@ -52,6 +52,7 @@ stages:
|
||||
--suppress=unreachableCode
|
||||
--suppress=internalAstError
|
||||
--suppress=integerOverflow
|
||||
--suppress=*:${CI_PROJECT_DIR}/infra/monitor/stellar-dump/*
|
||||
tags:
|
||||
- share
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ if (CMAKE_CXX_CPPCHECK)
|
||||
"--suppress=unreachableCode"
|
||||
"--suppress=internalAstError"
|
||||
"--suppress=integerOverflow"
|
||||
"--suppress=*:${CMAKE_SOURCE_DIR}/infra/monitor/stellar-dump/*"
|
||||
)
|
||||
set(CMAKE_C_CPPCHECK ${CMAKE_CXX_CPPCHECK})
|
||||
else()
|
||||
@@ -84,6 +85,7 @@ add_subdirectory(infra)
|
||||
add_subdirectory(decoders)
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(include)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(test)
|
||||
|
||||
install(DIRECTORY DESTINATION log COMPONENT PROGRAM)
|
||||
|
||||
@@ -65,6 +65,13 @@
|
||||
file = "log/stellar.log"
|
||||
level = "INFO" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
|
||||
|
||||
[monitor]
|
||||
listen_port = 80
|
||||
data_link_bind_port = 37008
|
||||
connection_idle_timeout = 60 # second
|
||||
cli_request_timeout = 3 # second
|
||||
pktdump_task_max_num = 3
|
||||
|
||||
[[module]]
|
||||
path = ""
|
||||
init = "packet_manager_on_init"
|
||||
@@ -78,3 +85,8 @@
|
||||
exit = "session_manager_on_exit"
|
||||
thread_init = "session_manager_on_thread_init"
|
||||
thread_exit = "session_manager_on_thread_exit"
|
||||
|
||||
[[module]]
|
||||
path=""
|
||||
init="monitor_on_init"
|
||||
exit="monitor_on_exit"
|
||||
|
||||
5
deps/CMakeLists.txt
vendored
5
deps/CMakeLists.txt
vendored
@@ -5,4 +5,7 @@ add_subdirectory(rbtree)
|
||||
add_subdirectory(interval_tree)
|
||||
add_subdirectory(bitmap)
|
||||
add_subdirectory(nmx_pool)
|
||||
add_subdirectory(logger)
|
||||
add_subdirectory(logger)
|
||||
add_subdirectory(sds)
|
||||
add_subdirectory(linenoise)
|
||||
add_subdirectory(ringbuf)
|
||||
2
deps/linenoise/CMakeLists.txt
vendored
Normal file
2
deps/linenoise/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
add_library(linenoise STATIC linenoise.c)
|
||||
target_include_directories(linenoise PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
1225
deps/linenoise/linenoise.c
vendored
Normal file
1225
deps/linenoise/linenoise.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
77
deps/linenoise/linenoise.h
vendored
Normal file
77
deps/linenoise/linenoise.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/* linenoise.h -- VERSION 1.0
|
||||
*
|
||||
* Guerrilla line editing library against the idea that a line editing lib
|
||||
* needs to be 20,000 lines of C code.
|
||||
*
|
||||
* See linenoise.c for more information.
|
||||
*
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis 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.
|
||||
*
|
||||
* 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
|
||||
* HOLDER 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 __LINENOISE_H
|
||||
#define __LINENOISE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct linenoiseCompletions
|
||||
{
|
||||
size_t len;
|
||||
char **cvec;
|
||||
} linenoiseCompletions;
|
||||
|
||||
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
|
||||
typedef char *(linenoiseHintsCallback)(const char *, int *color, int *bold);
|
||||
typedef void(linenoiseFreeHintsCallback)(void *);
|
||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
|
||||
char *linenoise(const char *prompt);
|
||||
void linenoiseFree(void *ptr);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
void linenoiseClearScreen(void);
|
||||
void linenoiseSetMultiLine(int ml);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LINENOISE_H */
|
||||
2
deps/ringbuf/CMakeLists.txt
vendored
Normal file
2
deps/ringbuf/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
add_library(ringbuf STATIC ringbuf.c)
|
||||
target_include_directories(ringbuf PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
430
deps/ringbuf/ringbuf.c
vendored
Normal file
430
deps/ringbuf/ringbuf.c
vendored
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Mindaugas Rasiukevicius <rmind at noxt eu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use is subject to license terms, as specified in the LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Atomic multi-producer single-consumer ring buffer, which supports
|
||||
* contiguous range operations and which can be conveniently used for
|
||||
* message passing.
|
||||
*
|
||||
* There are three offsets -- think of clock hands:
|
||||
* - NEXT: marks the beginning of the available space,
|
||||
* - WRITTEN: the point up to which the data is actually written.
|
||||
* - Observed READY: point up to which data is ready to be written.
|
||||
*
|
||||
* Producers
|
||||
*
|
||||
* Observe and save the 'next' offset, then request N bytes from
|
||||
* the ring buffer by atomically advancing the 'next' offset. Once
|
||||
* the data is written into the "reserved" buffer space, the thread
|
||||
* clears the saved value; these observed values are used to compute
|
||||
* the 'ready' offset.
|
||||
*
|
||||
* Consumer
|
||||
*
|
||||
* Writes the data between 'written' and 'ready' offsets and updates
|
||||
* the 'written' value. The consumer thread scans for the lowest
|
||||
* seen value by the producers.
|
||||
*
|
||||
* Key invariant
|
||||
*
|
||||
* Producers cannot go beyond the 'written' offset; producers are
|
||||
* also not allowed to catch up with the consumer. Only the consumer
|
||||
* is allowed to catch up with the producer i.e. set the 'written'
|
||||
* offset to be equal to the 'next' offset.
|
||||
*
|
||||
* Wrap-around
|
||||
*
|
||||
* If the producer cannot acquire the requested length due to little
|
||||
* available space at the end of the buffer, then it will wraparound.
|
||||
* WRAP_LOCK_BIT in 'next' offset is used to lock the 'end' offset.
|
||||
*
|
||||
* There is an ABA problem if one producer stalls while a pair of
|
||||
* producer and consumer would both successfully wrap-around and set
|
||||
* the 'next' offset to the stale value of the first producer, thus
|
||||
* letting it to perform a successful CAS violating the invariant.
|
||||
* A counter in the 'next' offset (masked by WRAP_COUNTER) is used
|
||||
* to prevent from this problem. It is incremented on wraparounds.
|
||||
*
|
||||
* The same ABA problem could also cause a stale 'ready' offset,
|
||||
* which could be observed by the consumer. We set WRAP_LOCK_BIT in
|
||||
* the 'seen' value before advancing the 'next' and clear this bit
|
||||
* after the successful advancing; this ensures that only the stable
|
||||
* 'ready' is observed by the consumer.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ringbuf.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define RBUF_OFF_MASK (0x00000000ffffffffUL)
|
||||
#define WRAP_LOCK_BIT (0x8000000000000000UL)
|
||||
#define RBUF_OFF_MAX (UINT64_MAX & ~WRAP_LOCK_BIT)
|
||||
|
||||
#define WRAP_COUNTER (0x7fffffff00000000UL)
|
||||
#define WRAP_INCR(x) (((x) + 0x100000000UL) & WRAP_COUNTER)
|
||||
|
||||
typedef uint64_t ringbuf_off_t;
|
||||
|
||||
struct ringbuf_worker {
|
||||
volatile ringbuf_off_t seen_off;
|
||||
int registered;
|
||||
};
|
||||
|
||||
struct ringbuf {
|
||||
/* Ring buffer space. */
|
||||
size_t space;
|
||||
|
||||
/*
|
||||
* The NEXT hand is atomically updated by the producer.
|
||||
* WRAP_LOCK_BIT is set in case of wrap-around; in such case,
|
||||
* the producer can update the 'end' offset.
|
||||
*/
|
||||
volatile ringbuf_off_t next;
|
||||
ringbuf_off_t end;
|
||||
|
||||
/* The following are updated by the consumer. */
|
||||
ringbuf_off_t written;
|
||||
unsigned nworkers;
|
||||
ringbuf_worker_t workers[];
|
||||
};
|
||||
|
||||
/*
|
||||
* ringbuf_setup: initialise a new ring buffer of a given length.
|
||||
*/
|
||||
int
|
||||
ringbuf_setup(ringbuf_t *rbuf, unsigned nworkers, size_t length)
|
||||
{
|
||||
if (length >= RBUF_OFF_MASK) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memset(rbuf, 0, offsetof(ringbuf_t, workers[nworkers]));
|
||||
rbuf->space = length;
|
||||
rbuf->end = RBUF_OFF_MAX;
|
||||
rbuf->nworkers = nworkers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ringbuf_get_sizes: return the sizes of the ringbuf_t and ringbuf_worker_t.
|
||||
*/
|
||||
void
|
||||
ringbuf_get_sizes(unsigned nworkers,
|
||||
size_t *ringbuf_size, size_t *ringbuf_worker_size)
|
||||
{
|
||||
if (ringbuf_size)
|
||||
*ringbuf_size = offsetof(ringbuf_t, workers[nworkers]);
|
||||
if (ringbuf_worker_size)
|
||||
*ringbuf_worker_size = sizeof(ringbuf_worker_t);
|
||||
}
|
||||
|
||||
/*
|
||||
* ringbuf_register: register the worker (thread/process) as a producer
|
||||
* and pass the pointer to its local store.
|
||||
*/
|
||||
ringbuf_worker_t *
|
||||
ringbuf_register(ringbuf_t *rbuf, unsigned i)
|
||||
{
|
||||
ringbuf_worker_t *w = &rbuf->workers[i];
|
||||
|
||||
w->seen_off = RBUF_OFF_MAX;
|
||||
atomic_store_explicit(&w->registered, true, memory_order_release);
|
||||
return w;
|
||||
}
|
||||
|
||||
void
|
||||
ringbuf_unregister(ringbuf_t *rbuf, ringbuf_worker_t *w)
|
||||
{
|
||||
w->registered = false;
|
||||
(void)rbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* stable_nextoff: capture and return a stable value of the 'next' offset.
|
||||
*/
|
||||
static inline ringbuf_off_t
|
||||
stable_nextoff(ringbuf_t *rbuf)
|
||||
{
|
||||
unsigned count = SPINLOCK_BACKOFF_MIN;
|
||||
ringbuf_off_t next;
|
||||
retry:
|
||||
next = atomic_load_explicit(&rbuf->next, memory_order_acquire);
|
||||
if (next & WRAP_LOCK_BIT) {
|
||||
SPINLOCK_BACKOFF(count);
|
||||
goto retry;
|
||||
}
|
||||
ASSERT((next & RBUF_OFF_MASK) < rbuf->space);
|
||||
return next;
|
||||
}
|
||||
|
||||
/*
|
||||
* stable_seenoff: capture and return a stable value of the 'seen' offset.
|
||||
*/
|
||||
static inline ringbuf_off_t
|
||||
stable_seenoff(ringbuf_worker_t *w)
|
||||
{
|
||||
unsigned count = SPINLOCK_BACKOFF_MIN;
|
||||
ringbuf_off_t seen_off;
|
||||
retry:
|
||||
seen_off = atomic_load_explicit(&w->seen_off, memory_order_acquire);
|
||||
if (seen_off & WRAP_LOCK_BIT) {
|
||||
SPINLOCK_BACKOFF(count);
|
||||
goto retry;
|
||||
}
|
||||
return seen_off;
|
||||
}
|
||||
|
||||
/*
|
||||
* ringbuf_acquire: request a space of a given length in the ring buffer.
|
||||
*
|
||||
* => On success: returns the offset at which the space is available.
|
||||
* => On failure: returns -1.
|
||||
*/
|
||||
ssize_t
|
||||
ringbuf_acquire(ringbuf_t *rbuf, ringbuf_worker_t *w, size_t len)
|
||||
{
|
||||
ringbuf_off_t seen, next, target;
|
||||
|
||||
ASSERT(len > 0 && len <= rbuf->space);
|
||||
ASSERT(w->seen_off == RBUF_OFF_MAX);
|
||||
|
||||
do {
|
||||
ringbuf_off_t written;
|
||||
|
||||
/*
|
||||
* Get the stable 'next' offset. Save the observed 'next'
|
||||
* value (i.e. the 'seen' offset), but mark the value as
|
||||
* unstable (set WRAP_LOCK_BIT).
|
||||
*
|
||||
* Note: CAS will issue a memory_order_release for us and
|
||||
* thus ensures that it reaches global visibility together
|
||||
* with new 'next'.
|
||||
*/
|
||||
seen = stable_nextoff(rbuf);
|
||||
next = seen & RBUF_OFF_MASK;
|
||||
ASSERT(next < rbuf->space);
|
||||
atomic_store_explicit(&w->seen_off, next | WRAP_LOCK_BIT,
|
||||
memory_order_relaxed);
|
||||
|
||||
/*
|
||||
* Compute the target offset. Key invariant: we cannot
|
||||
* go beyond the WRITTEN offset or catch up with it.
|
||||
*/
|
||||
target = next + len;
|
||||
written = rbuf->written;
|
||||
if (__predict_false(next < written && target >= written)) {
|
||||
/* The producer must wait. */
|
||||
atomic_store_explicit(&w->seen_off,
|
||||
RBUF_OFF_MAX, memory_order_release);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (__predict_false(target >= rbuf->space)) {
|
||||
const bool exceed = target > rbuf->space;
|
||||
|
||||
/*
|
||||
* Wrap-around and start from the beginning.
|
||||
*
|
||||
* If we would exceed the buffer, then attempt to
|
||||
* acquire the WRAP_LOCK_BIT and use the space in
|
||||
* the beginning. If we used all space exactly to
|
||||
* the end, then reset to 0.
|
||||
*
|
||||
* Check the invariant again.
|
||||
*/
|
||||
target = exceed ? (WRAP_LOCK_BIT | len) : 0;
|
||||
if ((target & RBUF_OFF_MASK) >= written) {
|
||||
atomic_store_explicit(&w->seen_off,
|
||||
RBUF_OFF_MAX, memory_order_release);
|
||||
return -1;
|
||||
}
|
||||
/* Increment the wrap-around counter. */
|
||||
target |= WRAP_INCR(seen & WRAP_COUNTER);
|
||||
} else {
|
||||
/* Preserve the wrap-around counter. */
|
||||
target |= seen & WRAP_COUNTER;
|
||||
}
|
||||
} while (!atomic_compare_exchange_weak(&rbuf->next, &seen, target));
|
||||
|
||||
/*
|
||||
* Acquired the range. Clear WRAP_LOCK_BIT in the 'seen' value
|
||||
* thus indicating that it is stable now.
|
||||
*
|
||||
* No need for memory_order_release, since CAS issued a fence.
|
||||
*/
|
||||
atomic_store_explicit(&w->seen_off, w->seen_off & ~WRAP_LOCK_BIT,
|
||||
memory_order_relaxed);
|
||||
|
||||
/*
|
||||
* If we set the WRAP_LOCK_BIT in the 'next' (because we exceed
|
||||
* the remaining space and need to wrap-around), then save the
|
||||
* 'end' offset and release the lock.
|
||||
*/
|
||||
if (__predict_false(target & WRAP_LOCK_BIT)) {
|
||||
/* Cannot wrap-around again if consumer did not catch-up. */
|
||||
ASSERT(rbuf->written <= next);
|
||||
ASSERT(rbuf->end == RBUF_OFF_MAX);
|
||||
rbuf->end = next;
|
||||
next = 0;
|
||||
|
||||
/*
|
||||
* Unlock: ensure the 'end' offset reaches global
|
||||
* visibility before the lock is released.
|
||||
*/
|
||||
atomic_store_explicit(&rbuf->next,
|
||||
(target & ~WRAP_LOCK_BIT), memory_order_release);
|
||||
}
|
||||
ASSERT((target & RBUF_OFF_MASK) <= rbuf->space);
|
||||
return (ssize_t)next;
|
||||
}
|
||||
|
||||
/*
|
||||
* ringbuf_produce: indicate the acquired range in the buffer is produced
|
||||
* and is ready to be consumed.
|
||||
*/
|
||||
void
|
||||
ringbuf_produce(ringbuf_t *rbuf, ringbuf_worker_t *w)
|
||||
{
|
||||
(void)rbuf;
|
||||
ASSERT(w->registered);
|
||||
ASSERT(w->seen_off != RBUF_OFF_MAX);
|
||||
atomic_store_explicit(&w->seen_off, RBUF_OFF_MAX, memory_order_release);
|
||||
}
|
||||
|
||||
/*
|
||||
* ringbuf_consume: get a contiguous range which is ready to be consumed.
|
||||
*/
|
||||
size_t
|
||||
ringbuf_consume(ringbuf_t *rbuf, size_t *offset)
|
||||
{
|
||||
ringbuf_off_t written = rbuf->written, next, ready;
|
||||
size_t towrite;
|
||||
retry:
|
||||
/*
|
||||
* Get the stable 'next' offset. Note: stable_nextoff() issued
|
||||
* a load memory barrier. The area between the 'written' offset
|
||||
* and the 'next' offset will be the *preliminary* target buffer
|
||||
* area to be consumed.
|
||||
*/
|
||||
next = stable_nextoff(rbuf) & RBUF_OFF_MASK;
|
||||
if (written == next) {
|
||||
/* If producers did not advance, then nothing to do. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Observe the 'ready' offset of each producer.
|
||||
*
|
||||
* At this point, some producer might have already triggered the
|
||||
* wrap-around and some (or all) seen 'ready' values might be in
|
||||
* the range between 0 and 'written'. We have to skip them.
|
||||
*/
|
||||
ready = RBUF_OFF_MAX;
|
||||
|
||||
for (unsigned i = 0; i < rbuf->nworkers; i++) {
|
||||
ringbuf_worker_t *w = &rbuf->workers[i];
|
||||
ringbuf_off_t seen_off;
|
||||
|
||||
/*
|
||||
* Skip if the worker has not registered.
|
||||
*
|
||||
* Get a stable 'seen' value. This is necessary since we
|
||||
* want to discard the stale 'seen' values.
|
||||
*/
|
||||
if (!atomic_load_explicit(&w->registered, memory_order_relaxed))
|
||||
continue;
|
||||
seen_off = stable_seenoff(w);
|
||||
|
||||
/*
|
||||
* Ignore the offsets after the possible wrap-around.
|
||||
* We are interested in the smallest seen offset that is
|
||||
* not behind the 'written' offset.
|
||||
*/
|
||||
if (seen_off >= written) {
|
||||
ready = MIN(seen_off, ready);
|
||||
}
|
||||
ASSERT(ready >= written);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, we need to determine whether wrap-around occurred
|
||||
* and deduct the safe 'ready' offset.
|
||||
*/
|
||||
if (next < written) {
|
||||
const ringbuf_off_t end = MIN(rbuf->space, rbuf->end);
|
||||
|
||||
/*
|
||||
* Wrap-around case. Check for the cut off first.
|
||||
*
|
||||
* Reset the 'written' offset if it reached the end of
|
||||
* the buffer or the 'end' offset (if set by a producer).
|
||||
* However, we must check that the producer is actually
|
||||
* done (the observed 'ready' offsets are clear).
|
||||
*/
|
||||
if (ready == RBUF_OFF_MAX && written == end) {
|
||||
/*
|
||||
* Clear the 'end' offset if was set.
|
||||
*/
|
||||
if (rbuf->end != RBUF_OFF_MAX) {
|
||||
rbuf->end = RBUF_OFF_MAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrap-around the consumer and start from zero.
|
||||
*/
|
||||
written = 0;
|
||||
atomic_store_explicit(&rbuf->written,
|
||||
written, memory_order_release);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot wrap-around yet; there is data to consume at
|
||||
* the end. The ready range is smallest of the observed
|
||||
* 'ready' or the 'end' offset. If neither is set, then
|
||||
* the actual end of the buffer.
|
||||
*/
|
||||
ASSERT(ready > next);
|
||||
ready = MIN(ready, end);
|
||||
ASSERT(ready >= written);
|
||||
} else {
|
||||
/*
|
||||
* Regular case. Up to the observed 'ready' (if set)
|
||||
* or the 'next' offset.
|
||||
*/
|
||||
ready = MIN(ready, next);
|
||||
}
|
||||
towrite = ready - written;
|
||||
*offset = written;
|
||||
|
||||
ASSERT(ready >= written);
|
||||
ASSERT(towrite <= rbuf->space);
|
||||
return towrite;
|
||||
}
|
||||
|
||||
/*
|
||||
* ringbuf_release: indicate that the consumed range can now be released.
|
||||
*/
|
||||
void
|
||||
ringbuf_release(ringbuf_t *rbuf, size_t nbytes)
|
||||
{
|
||||
const size_t nwritten = rbuf->written + nbytes;
|
||||
|
||||
ASSERT(rbuf->written <= rbuf->space);
|
||||
ASSERT(rbuf->written <= rbuf->end);
|
||||
ASSERT(nwritten <= rbuf->space);
|
||||
|
||||
rbuf->written = (nwritten == rbuf->space) ? 0 : nwritten;
|
||||
}
|
||||
29
deps/ringbuf/ringbuf.h
vendored
Normal file
29
deps/ringbuf/ringbuf.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use is subject to license terms, as specified in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef _RINGBUF_H_
|
||||
#define _RINGBUF_H_
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
typedef struct ringbuf ringbuf_t;
|
||||
typedef struct ringbuf_worker ringbuf_worker_t;
|
||||
|
||||
int ringbuf_setup(ringbuf_t *, unsigned, size_t);
|
||||
void ringbuf_get_sizes(unsigned, size_t *, size_t *);
|
||||
|
||||
ringbuf_worker_t *ringbuf_register(ringbuf_t *, unsigned);
|
||||
void ringbuf_unregister(ringbuf_t *, ringbuf_worker_t *);
|
||||
|
||||
ssize_t ringbuf_acquire(ringbuf_t *, ringbuf_worker_t *, size_t);
|
||||
void ringbuf_produce(ringbuf_t *, ringbuf_worker_t *);
|
||||
size_t ringbuf_consume(ringbuf_t *, size_t *);
|
||||
void ringbuf_release(ringbuf_t *, size_t);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
115
deps/ringbuf/utils.h
vendored
Normal file
115
deps/ringbuf/utils.h
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Berkeley Software Design, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. Neither the name of the University 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 REGENTS 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 REGENTS 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.
|
||||
*
|
||||
* @(#)cdefs.h 8.8 (Berkeley) 1/9/95
|
||||
*/
|
||||
|
||||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* A regular assert (debug/diagnostic only).
|
||||
*/
|
||||
#if defined(DEBUG)
|
||||
#define ASSERT assert
|
||||
#else
|
||||
#define ASSERT(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Minimum, maximum and rounding macros.
|
||||
*/
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Branch prediction macros.
|
||||
*/
|
||||
#ifndef __predict_true
|
||||
#define __predict_true(x) __builtin_expect((x) != 0, 1)
|
||||
#define __predict_false(x) __builtin_expect((x) != 0, 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Atomic operations and memory barriers. If C11 API is not available,
|
||||
* then wrap the GCC builtin routines.
|
||||
*
|
||||
* Note: This atomic_compare_exchange_weak does not do the C11 thing of
|
||||
* filling *(expected) with the actual value, because we don't need
|
||||
* that here.
|
||||
*/
|
||||
#ifndef atomic_compare_exchange_weak
|
||||
#define atomic_compare_exchange_weak(ptr, expected, desired) \
|
||||
__sync_bool_compare_and_swap(ptr, *(expected), desired)
|
||||
#endif
|
||||
|
||||
#ifndef atomic_thread_fence
|
||||
#define memory_order_relaxed __ATOMIC_RELAXED
|
||||
#define memory_order_acquire __ATOMIC_ACQUIRE
|
||||
#define memory_order_release __ATOMIC_RELEASE
|
||||
#define memory_order_seq_cst __ATOMIC_SEQ_CST
|
||||
#define atomic_thread_fence(m) __atomic_thread_fence(m)
|
||||
#endif
|
||||
#ifndef atomic_store_explicit
|
||||
#define atomic_store_explicit __atomic_store_n
|
||||
#endif
|
||||
#ifndef atomic_load_explicit
|
||||
#define atomic_load_explicit __atomic_load_n
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Exponential back-off for the spinning paths.
|
||||
*/
|
||||
#define SPINLOCK_BACKOFF_MIN 4
|
||||
#define SPINLOCK_BACKOFF_MAX 128
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#define SPINLOCK_BACKOFF_HOOK __asm volatile("pause" ::: "memory")
|
||||
#else
|
||||
#define SPINLOCK_BACKOFF_HOOK
|
||||
#endif
|
||||
#define SPINLOCK_BACKOFF(count) \
|
||||
do { \
|
||||
for (int __i = (count); __i != 0; __i--) { \
|
||||
SPINLOCK_BACKOFF_HOOK; \
|
||||
} \
|
||||
if ((count) < SPINLOCK_BACKOFF_MAX) \
|
||||
(count) += (count); \
|
||||
} while (/* CONSTCOND */ 0);
|
||||
|
||||
#endif
|
||||
2
deps/sds/CMakeLists.txt
vendored
Normal file
2
deps/sds/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
add_library(sds STATIC sds.c)
|
||||
target_include_directories(sds PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
1335
deps/sds/sds.c
vendored
Normal file
1335
deps/sds/sds.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
285
deps/sds/sds.h
vendored
Normal file
285
deps/sds/sds.h
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
/* https://github.com/antirez/sds
|
||||
* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024)
|
||||
extern const char *SDS_NOINIT;
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef char *sds;
|
||||
|
||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
||||
* However is here to document the layout of type 5 SDS strings. */
|
||||
struct __attribute__ ((__packed__)) sdshdr5 {
|
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr8 {
|
||||
uint8_t len; /* used */
|
||||
uint8_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr16 {
|
||||
uint16_t len; /* used */
|
||||
uint16_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr32 {
|
||||
uint32_t len; /* used */
|
||||
uint32_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
uint64_t len; /* used */
|
||||
uint64_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
|
||||
#define SDS_TYPE_5 0
|
||||
#define SDS_TYPE_8 1
|
||||
#define SDS_TYPE_16 2
|
||||
#define SDS_TYPE_32 3
|
||||
#define SDS_TYPE_64 4
|
||||
#define SDS_TYPE_MASK 7
|
||||
#define SDS_TYPE_BITS 3
|
||||
// #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
|
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
|
||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
|
||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
|
||||
|
||||
static inline size_t sdslen(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
return SDS_TYPE_5_LEN(flags);
|
||||
case SDS_TYPE_8:
|
||||
return SDS_HDR(8,s)->len;
|
||||
case SDS_TYPE_16:
|
||||
return SDS_HDR(16,s)->len;
|
||||
case SDS_TYPE_32:
|
||||
return SDS_HDR(32,s)->len;
|
||||
case SDS_TYPE_64:
|
||||
return SDS_HDR(64,s)->len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5: {
|
||||
return 0;
|
||||
}
|
||||
case SDS_TYPE_8: {
|
||||
SDS_HDR_VAR(8,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_16: {
|
||||
SDS_HDR_VAR(16,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_32: {
|
||||
SDS_HDR_VAR(32,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_64: {
|
||||
SDS_HDR_VAR(64,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdssetlen(sds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len = newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sdsinclen(sds s, size_t inc) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len += inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* sdsalloc() = sdsavail() + sdslen() */
|
||||
static inline size_t sdsalloc(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
return SDS_TYPE_5_LEN(flags);
|
||||
case SDS_TYPE_8:
|
||||
return SDS_HDR(8,s)->alloc;
|
||||
case SDS_TYPE_16:
|
||||
return SDS_HDR(16,s)->alloc;
|
||||
case SDS_TYPE_32:
|
||||
return SDS_HDR(32,s)->alloc;
|
||||
case SDS_TYPE_64:
|
||||
return SDS_HDR(64,s)->alloc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdssetalloc(sds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->alloc = newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty(void);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(sds s);
|
||||
sds sdsgrowzero(sds s, size_t len);
|
||||
sds sdscatlen(sds s, const void *t, size_t len);
|
||||
sds sdscat(sds s, const char *t);
|
||||
sds sdscatsds(sds s, const sds t);
|
||||
sds sdscpylen(sds s, const char *t, size_t len);
|
||||
sds sdscpy(sds s, const char *t);
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
sds sdscatfmt(sds s, const char *fmt, ...);
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
void sdsrange(sds s, ssize_t start, ssize_t end);
|
||||
void sdsupdatelen(sds s);
|
||||
void sdsclear(sds s);
|
||||
int sdscmp(const sds s1, const sds s2);
|
||||
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);
|
||||
void sdsfreesplitres(sds *tokens, int count);
|
||||
void sdstolower(sds s);
|
||||
void sdstoupper(sds s);
|
||||
sds sdsfromlonglong(long long value);
|
||||
sds sdscatrepr(sds s, const char *p, size_t len);
|
||||
sds *sdssplitargs(const char *line, int *argc);
|
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
|
||||
sds sdsjoin(char **argv, int argc, char *sep);
|
||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
||||
void sdsIncrLen(sds s, ssize_t incr);
|
||||
sds sdsRemoveFreeSpace(sds s);
|
||||
size_t sdsAllocSize(sds s);
|
||||
void *sdsAllocPtr(sds s);
|
||||
|
||||
/* Export the allocator used by SDS to the program using SDS.
|
||||
* Sometimes the program SDS is linked to, may use a different set of
|
||||
* allocators, but may want to allocate or free things that SDS will
|
||||
* respectively free or allocate. */
|
||||
void *sds_malloc(size_t size);
|
||||
void *sds_realloc(void *ptr, size_t size);
|
||||
void sds_free(void *ptr);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int sdsTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
42
deps/sds/sdsalloc.h
vendored
Normal file
42
deps/sds/sdsalloc.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* SDS allocator selection.
|
||||
*
|
||||
* This file is used in order to change the SDS allocator at compile time.
|
||||
* Just define the following defines to what you want to use. Also add
|
||||
* the include of your alternate allocator if needed (not needed in order
|
||||
* to use the default libc allocator). */
|
||||
|
||||
#define s_malloc malloc
|
||||
#define s_realloc realloc
|
||||
#define s_free free
|
||||
57
deps/sds/testhelp.h
vendored
Normal file
57
deps/sds/testhelp.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/* This is a really minimal testing framework for C.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* test_cond("Check if 1 == 1", 1==1)
|
||||
* test_cond("Check if 5 > 10", 5 > 10)
|
||||
* test_report()
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2012, Salvatore Sanfilippo <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 __TESTHELP_H
|
||||
#define __TESTHELP_H
|
||||
|
||||
int __failed_tests = 0;
|
||||
int __test_num = 0;
|
||||
#define test_cond(descr,_c) do { \
|
||||
__test_num++; printf("%d - %s: ", __test_num, descr); \
|
||||
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
|
||||
} while(0);
|
||||
#define test_report() do { \
|
||||
printf("%d tests, %d passed, %d failed\n", __test_num, \
|
||||
__test_num-__failed_tests, __failed_tests); \
|
||||
if (__failed_tests) { \
|
||||
printf("=== WARNING === We have failed tests here...\n"); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
#endif
|
||||
54
include/stellar/monitor.h
Normal file
54
include/stellar/monitor.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "stellar/module.h"
|
||||
#define MONITOR_MODULE_NAME "monitor"
|
||||
struct stellar_monitor;
|
||||
/* use stellar_module_manager_get_module() to get monitor_module */
|
||||
struct stellar_monitor *monitor_module_to_monitor(struct module *monitor_module);
|
||||
|
||||
#define error_format_server_close "ERR Server closed the connection"
|
||||
#define error_format_connection_error "ERR could not connect to '%s:%u' : Connection refused"
|
||||
#define error_format_unknown_command "ERR unknown command '%s'"
|
||||
#define error_format_unknown_arg "ERR unrecognized args '%s'"
|
||||
#define error_format_wrong_number_of_args "ERR wrong number of arguments for '%s' command"
|
||||
#define error_format_arg_not_valid_integer "ERR arg '%s' is not an integer or out of range"
|
||||
|
||||
struct monitor_reply;
|
||||
struct monitor_reply *monitor_reply_nil(void);
|
||||
struct monitor_reply *monitor_reply_new_string(const char *format, ...);
|
||||
struct monitor_reply *monitor_reply_new_integer(long long integer);
|
||||
struct monitor_reply *monitor_reply_new_double(double dval);
|
||||
struct monitor_reply *monitor_reply_new_error(const char *format, ...); /* for common errors, "error_format_xxx" should be used */
|
||||
struct monitor_reply *monitor_reply_new_status(const char *format, ...);
|
||||
|
||||
/* Like Linux bash, argv[0] is always the command name itself */
|
||||
typedef struct monitor_reply *(monitor_cmd_cb)(struct stellar_monitor *monitor, int argc, char *argv[], void *arg);
|
||||
|
||||
/*
|
||||
* All command characters are case-insensitive.
|
||||
* The set of flags 'flags' specify the behavior of the command, and should be
|
||||
* passed as a C string composed of space separated words, The set of flags are:
|
||||
*
|
||||
* "write" : The command may modify the data set (it may also read from it).
|
||||
* "readonly": The command returns data from keys but never writes.
|
||||
*
|
||||
* return value:
|
||||
* 0: success
|
||||
* -1: failed
|
||||
* 1: already exist, has been registered
|
||||
*/
|
||||
int monitor_register_cmd(struct stellar_monitor *monitor, const char *cmd, monitor_cmd_cb *cb, const char *flags,
|
||||
const char *hint, const char *description, void *arg);
|
||||
|
||||
/*
|
||||
All modules should support "-h/--help/help" args to output usage information.
|
||||
"show modulea help"
|
||||
"show moduleb -h"
|
||||
"show modulec --help"
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
set(INFRA exdata mq tuple packet_manager packet_io ip_reassembly tcp_reassembly session_manager module_manager)
|
||||
set(DEPS bitmap dablooms interval_tree logger nmx_pool rbtree timeout toml)
|
||||
set(INFRA exdata mq tuple packet_manager packet_io ip_reassembly tcp_reassembly session_manager module_manager monitor)
|
||||
set(DEPS bitmap dablooms interval_tree logger nmx_pool rbtree timeout toml ringbuf)
|
||||
set(DECODERS lpi_plus)
|
||||
set(WHOLE_ARCHIVE ${DEPS} ${INFRA} ${DECODERS})
|
||||
set(LIBS fieldstat4)
|
||||
|
||||
22
infra/monitor/CMakeLists.txt
Normal file
22
infra/monitor/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# add_subdirectory(enforcer)
|
||||
|
||||
add_library(monitor
|
||||
monitor_cmd_assistant.c
|
||||
monitor_transaction.c
|
||||
monitor_server.c
|
||||
monitor_utils.c
|
||||
monitor_stat.c
|
||||
monitor_spinlock.c
|
||||
monitor_ringbuf.c
|
||||
monitor_rpc.c
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include/)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/infra)
|
||||
target_include_directories(monitor PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
set_target_properties(monitor PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/infra/monitor/version.map")
|
||||
set_target_properties(monitor PROPERTIES PREFIX "")
|
||||
target_link_libraries(monitor toml sds linenoise tuple session_manager libevent-static libevent-static cjson-static ringbuf)
|
||||
target_link_options(monitor PRIVATE -rdynamic)
|
||||
11
infra/monitor/enforcer/CMakeLists.txt
Normal file
11
infra/monitor/enforcer/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
add_library(monitor_enforcer STATIC show_session_enforcer.c )
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include/)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/uthash)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/timeout)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/infra)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/infra/tuple/)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/infra/packet_manager)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/infra/tcp_reassembly)
|
||||
|
||||
target_include_directories(monitor_enforcer PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
818
infra/monitor/enforcer/show_session_enforcer.c
Normal file
818
infra/monitor/enforcer/show_session_enforcer.c
Normal file
@@ -0,0 +1,818 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include "stellar/session.h"
|
||||
#include "stellar/monitor.h"
|
||||
#include "session_manager/session_manager_rte.h"
|
||||
#include "monitor/monitor_utils.h"
|
||||
#include "monitor/monitor_rpc.h"
|
||||
#include "sds/sds.h"
|
||||
#include "session_manager/session_internal.h"
|
||||
|
||||
// temp add
|
||||
extern struct session_manager_rte *session_manager_get_runtime(struct session_manager *sess_mgr, uint16_t thread_id);
|
||||
|
||||
#define SHOW_SESSION_BRIEF_LIMIT_DEFAULT 10
|
||||
#define SHOW_SESSION_BRIEF_LIMIT_MAX 1000
|
||||
#define SHOW_SESSION_BRIEF_SCAN_COUNT_DEFAULT 100
|
||||
|
||||
/* show session brief */
|
||||
struct stm_show_session_brief_opt
|
||||
{
|
||||
#define SESSION_SCAN_SNET (1 << 25)
|
||||
#define SESSION_SCAN_DNET (1 << 26)
|
||||
#define SESSION_SCAN_ID (1 << 27)
|
||||
#define SESSION_SCAN_CURSOR (1 << 28)
|
||||
#define SESSION_SCAN_COUNT (1 << 29)
|
||||
#define SESSION_SCAN_LIMIT (1 << 30)
|
||||
struct session_scan_opts scan_opt;
|
||||
uint32_t limit;
|
||||
};
|
||||
|
||||
/* show session detail <id> */
|
||||
struct stm_show_session_detail_opt
|
||||
{
|
||||
uint64_t sess_id;
|
||||
int thread_idx; // todo, not used now
|
||||
};
|
||||
|
||||
struct show_session_brief_result
|
||||
{
|
||||
uint64_t sid;
|
||||
int thread_index;
|
||||
enum session_state state;
|
||||
enum session_type protocol;
|
||||
time_t create_time_in_sec;
|
||||
time_t last_pkt_time_in_sec;
|
||||
uint8_t flow_dir;
|
||||
struct tuple6 addr;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct show_session_brief_array
|
||||
{
|
||||
size_t array_num;
|
||||
struct show_session_brief_result array[0]; /* Continuous memory, array_num * sizeof(struct show_session_brief_result) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct show_session_detail_result
|
||||
{
|
||||
struct show_session_brief_result sess_brief;
|
||||
unsigned long long sess_stat[MAX_FLOW_TYPE][MAX_STAT];
|
||||
enum session_direction direction; // in or out
|
||||
unsigned char application; // L7 appid
|
||||
// todo, other info
|
||||
} __attribute__((packed));
|
||||
|
||||
static void stm_session_brief_cli_args_set_default(struct stm_show_session_brief_opt *show_opt)
|
||||
{
|
||||
memset(show_opt, 0, sizeof(struct stm_show_session_brief_opt));
|
||||
show_opt->limit = SHOW_SESSION_BRIEF_LIMIT_DEFAULT;
|
||||
show_opt->scan_opt.cursor = 0;
|
||||
show_opt->scan_opt.count = SHOW_SESSION_BRIEF_SCAN_COUNT_DEFAULT;
|
||||
}
|
||||
|
||||
static uint32_t stm_session_brief_cli_args_get_flags(const char *para_name)
|
||||
{
|
||||
if (NULL == para_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncasecmp(para_name, "id", 2) == 0)
|
||||
{
|
||||
return SESSION_SCAN_ID;
|
||||
}
|
||||
else if (strncasecmp(para_name, "cursor", 6) == 0)
|
||||
{
|
||||
return SESSION_SCAN_CURSOR;
|
||||
}
|
||||
else if (strncasecmp(para_name, "count", 5) == 0)
|
||||
{
|
||||
return SESSION_SCAN_COUNT;
|
||||
}
|
||||
else if (strncasecmp(para_name, "state", 5) == 0)
|
||||
{
|
||||
return SESSION_SCAN_STATE;
|
||||
}
|
||||
else if (strncasecmp(para_name, "sip", 3) == 0)
|
||||
{
|
||||
return SESSION_SCAN_SIP;
|
||||
}
|
||||
else if (strncasecmp(para_name, "dip", 3) == 0)
|
||||
{
|
||||
return SESSION_SCAN_DIP;
|
||||
}
|
||||
else if (strncasecmp(para_name, "snet", 4) == 0)
|
||||
{
|
||||
return SESSION_SCAN_SNET;
|
||||
}
|
||||
else if (strncasecmp(para_name, "dnet", 4) == 0)
|
||||
{
|
||||
return SESSION_SCAN_DNET;
|
||||
}
|
||||
else if (strncasecmp(para_name, "sport", 5) == 0)
|
||||
{
|
||||
return SESSION_SCAN_SPORT;
|
||||
}
|
||||
else if (strncasecmp(para_name, "dport", 5) == 0)
|
||||
{
|
||||
return SESSION_SCAN_DPORT;
|
||||
}
|
||||
else if (strncasecmp(para_name, "protocol", 8) == 0)
|
||||
{
|
||||
return SESSION_SCAN_TYPE;
|
||||
}
|
||||
else if (strncasecmp(para_name, "limit", 5) == 0)
|
||||
{
|
||||
return SESSION_SCAN_LIMIT;
|
||||
}
|
||||
else if (strncasecmp(para_name, "create-time-in", strlen("create-time-in")) == 0)
|
||||
{
|
||||
return SESSION_SCAN_CREATE_TIME;
|
||||
}
|
||||
else if (strncasecmp(para_name, "last-pkt-time-in", strlen("last-pkt-time-in")) == 0)
|
||||
{
|
||||
return SESSION_SCAN_LASPKT_TIME;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum session_state show_session_state_pton(const char *state_str)
|
||||
{
|
||||
if (strncasecmp(state_str, "opening", strlen("opening")) == 0)
|
||||
{
|
||||
return SESSION_STATE_OPENING;
|
||||
}
|
||||
else if (strncasecmp(state_str, "active", strlen("active")) == 0)
|
||||
{
|
||||
return SESSION_STATE_ACTIVE;
|
||||
}
|
||||
else if (strncasecmp(state_str, "closing", strlen("closing")) == 0)
|
||||
{
|
||||
return SESSION_STATE_CLOSING;
|
||||
}
|
||||
|
||||
return MAX_STATE;
|
||||
}
|
||||
|
||||
static uint32_t show_session_ipaddr_ntop(const char *ip_string, struct session_scan_opts *scan_opt, uint32_t flag)
|
||||
{
|
||||
uint32_t addr_family;
|
||||
if (SESSION_SCAN_SIP == flag)
|
||||
{
|
||||
addr_family = stm_inet_pton(ip_string, &scan_opt->src_addr[0].v4, &scan_opt->src_addr[0].v6);
|
||||
}
|
||||
else
|
||||
{
|
||||
addr_family = stm_inet_pton(ip_string, &scan_opt->dst_addr[0].v4, &scan_opt->dst_addr[0].v6);
|
||||
}
|
||||
|
||||
if (addr_family == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (AF_INET == addr_family)
|
||||
{
|
||||
if (SESSION_SCAN_SIP == flag)
|
||||
{
|
||||
scan_opt->src_addr[1].v4 = scan_opt->src_addr[0].v4;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_opt->dst_addr[1].v4 = scan_opt->dst_addr[0].v4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SESSION_SCAN_SIP == flag)
|
||||
{
|
||||
scan_opt->src_addr[1].v6 = scan_opt->src_addr[0].v6;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_opt->dst_addr[1].v6 = scan_opt->dst_addr[0].v6;
|
||||
}
|
||||
}
|
||||
return addr_family;
|
||||
}
|
||||
|
||||
static sds show_session_cli_args_sanity_check(const struct stm_show_session_brief_opt *brief_opt)
|
||||
{
|
||||
uint32_t flags = brief_opt->scan_opt.flags;
|
||||
sds ss = sdsempty();
|
||||
|
||||
if ((flags & SESSION_SCAN_SIP) && (flags & SESSION_SCAN_SNET))
|
||||
{
|
||||
ss = sdscatprintf(ss, "error: the 'sip' and 'snet' options conflict!");
|
||||
return ss;
|
||||
}
|
||||
if ((flags & SESSION_SCAN_DIP) && (flags & SESSION_SCAN_DNET))
|
||||
{
|
||||
ss = sdscatprintf(ss, "error: the 'dip' and 'dnet' options conflict!");
|
||||
return ss;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
static int show_session_is_help(int argc, char *argv[])
|
||||
{
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
if (strncasecmp(argv[i], "help", 4) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (strncasecmp(argv[i], "--help", 6) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (strncasecmp(argv[i], "-h", 2) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct monitor_reply *show_session_brief_usage(void)
|
||||
{
|
||||
sds ss = sdsempty();
|
||||
ss = sdscatprintf(ss, "Usage: show session brief [options]\n");
|
||||
ss = sdscatprintf(ss, "Options:\n");
|
||||
ss = sdscatprintf(ss, " -h, --help help\tdisplay usage information and exit\n");
|
||||
ss = sdscatprintf(ss, " cursor <cursor>\n");
|
||||
ss = sdscatprintf(ss, " count <count>\n");
|
||||
ss = sdscatprintf(ss, " state <opening|active|closing>\n");
|
||||
ss = sdscatprintf(ss, " protocol <tcp|udp>\n");
|
||||
ss = sdscatprintf(ss, " sip <source ip>\n");
|
||||
ss = sdscatprintf(ss, " dip <destination ip>\n");
|
||||
ss = sdscatprintf(ss, " sport <source port>\n");
|
||||
ss = sdscatprintf(ss, " dport <destination port>\n");
|
||||
ss = sdscatprintf(ss, " snet <source network/netmask>\texample 192.168.1.0/24\n");
|
||||
ss = sdscatprintf(ss, " dnet <destination network/netmask>\texample 1234::abcd/48\n");
|
||||
ss = sdscatprintf(ss, " create-time-in <last-N-[seconds|minutes|hours|days]>\texample last-1-hours\n");
|
||||
ss = sdscatprintf(ss, " last-pkt-time-in <last-N-[seconds|minutes|hours|days]>\texample last-7-days\n");
|
||||
ss = sdscatprintf(ss, " limit <limit>\n");
|
||||
|
||||
struct monitor_reply *reply = monitor_reply_new_string("%s", ss);
|
||||
sdsfree(ss);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static struct monitor_reply *show_session_detail_usage(void)
|
||||
{
|
||||
sds ss = sdsempty();
|
||||
ss = sdscatprintf(ss, "Usage: show session detial [options]\n");
|
||||
ss = sdscatprintf(ss, "Options:\n");
|
||||
ss = sdscatprintf(ss, " -h, --help help\tdisplay usage information and exit\n");
|
||||
ss = sdscatprintf(ss, " id <session id>\n");
|
||||
struct monitor_reply *reply = monitor_reply_new_string("%s", ss);
|
||||
sdsfree(ss);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
"show session ..." command args parser
|
||||
return:
|
||||
NULL: success, brief_opt is filled
|
||||
not NULL: error message
|
||||
*/
|
||||
static struct monitor_reply *show_session_brief_cli_args_parse(int argc, char *argv[], struct stm_show_session_brief_opt *brief_opt)
|
||||
{
|
||||
uint32_t ipaddr_family = 0, history_ipaddr_family = 0;
|
||||
sds ss = NULL;
|
||||
uint32_t flag;
|
||||
struct monitor_reply *error_reply = NULL;
|
||||
const char *opt_name, *opt_value;
|
||||
|
||||
stm_session_brief_cli_args_set_default(brief_opt);
|
||||
int i = 3; // skip "show session brief"
|
||||
while (i < argc)
|
||||
{
|
||||
ipaddr_family = 0;
|
||||
opt_name = argv[i];
|
||||
flag = stm_session_brief_cli_args_get_flags(opt_name);
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("option %s requires an argument\n", opt_name);
|
||||
return error_reply;
|
||||
}
|
||||
opt_value = argv[++i];
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case SESSION_SCAN_TYPE:
|
||||
{
|
||||
if (strncasecmp(opt_value, "tcp", 3) == 0)
|
||||
{
|
||||
brief_opt->scan_opt.type = SESSION_TYPE_TCP;
|
||||
}
|
||||
else if (strncasecmp(opt_value, "udp", 3) == 0)
|
||||
{
|
||||
brief_opt->scan_opt.type = SESSION_TYPE_UDP;
|
||||
}
|
||||
else
|
||||
{
|
||||
error_reply = monitor_reply_new_error("unsupported protocol type: %s. should be <tcp|udp>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_STATE:
|
||||
{
|
||||
enum session_state tmp_state = show_session_state_pton(opt_value);
|
||||
if (tmp_state == MAX_STATE)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("unrecognized session state: %s. should be <opening|active|closing>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.state = tmp_state;
|
||||
}
|
||||
break;
|
||||
// case SESSION_SCAN_ID:
|
||||
// if (stm_string_isdigit(opt_value) == 0)
|
||||
// {
|
||||
// *error_reply = monitor_reply_new_error("invalid session id: %s. should be integer in range: <1 - UINT64_MAX>\n", opt_value);
|
||||
// goto error_exit;
|
||||
// }
|
||||
// show_opt->detail_opt.sess_id = strtoull(opt_value, NULL, 10);
|
||||
// break;
|
||||
case SESSION_SCAN_SIP:
|
||||
ipaddr_family = show_session_ipaddr_ntop(opt_value, &brief_opt->scan_opt, SESSION_SCAN_SIP);
|
||||
if (ipaddr_family == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("invalid sip address: %s\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.addr_family = ipaddr_family;
|
||||
break;
|
||||
case SESSION_SCAN_DIP:
|
||||
ipaddr_family = show_session_ipaddr_ntop(opt_value, &brief_opt->scan_opt, SESSION_SCAN_DIP);
|
||||
if (ipaddr_family == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("invalid dip address: %s\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.addr_family = ipaddr_family;
|
||||
break;
|
||||
|
||||
case SESSION_SCAN_SNET:
|
||||
{
|
||||
uint32_t ipv4addr, ipv4mask;
|
||||
struct in6_addr ipv6addr, ipv6mask;
|
||||
ipaddr_family = stm_ip_cidr_pton(opt_value, &ipv4addr, &ipv4mask, &ipv6addr, &ipv6mask);
|
||||
if (ipaddr_family == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("invalid snet CIDR address: %s\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
if (AF_INET == ipaddr_family)
|
||||
{
|
||||
uint32_t ipv4_range[2];
|
||||
stm_ipv4_cidr_to_range(ipv4addr, ipv4mask, ipv4_range);
|
||||
brief_opt->scan_opt.src_addr[0].v4.s_addr = ipv4_range[0];
|
||||
brief_opt->scan_opt.src_addr[1].v4.s_addr = ipv4_range[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in6_addr ipv6_range[2];
|
||||
stm_ipv6_cidr_to_range(&ipv6addr, &ipv6mask, ipv6_range);
|
||||
brief_opt->scan_opt.src_addr[0].v6 = ipv6_range[0];
|
||||
brief_opt->scan_opt.src_addr[1].v6 = ipv6_range[1];
|
||||
}
|
||||
brief_opt->scan_opt.addr_family = ipaddr_family;
|
||||
flag = SESSION_SCAN_SIP;
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_DNET:
|
||||
{
|
||||
uint32_t ipv4addr, ipv4mask;
|
||||
struct in6_addr ipv6addr, ipv6mask;
|
||||
ipaddr_family = stm_ip_cidr_pton(opt_value, &ipv4addr, &ipv4mask, &ipv6addr, &ipv6mask);
|
||||
if (ipaddr_family == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("invalid dnet CIDR address: %s\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
if (AF_INET == ipaddr_family)
|
||||
{
|
||||
uint32_t ipv4_range[2];
|
||||
stm_ipv4_cidr_to_range(ipv4addr, ipv4mask, ipv4_range);
|
||||
brief_opt->scan_opt.dst_addr[0].v4.s_addr = ipv4_range[0];
|
||||
brief_opt->scan_opt.dst_addr[1].v4.s_addr = ipv4_range[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in6_addr ipv6_range[2];
|
||||
stm_ipv6_cidr_to_range(&ipv6addr, &ipv6mask, ipv6_range);
|
||||
brief_opt->scan_opt.dst_addr[0].v6 = ipv6_range[0];
|
||||
brief_opt->scan_opt.dst_addr[1].v6 = ipv6_range[1];
|
||||
}
|
||||
brief_opt->scan_opt.addr_family = ipaddr_family;
|
||||
flag = SESSION_SCAN_DIP;
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_SPORT:
|
||||
{
|
||||
if (stm_string_isdigit(opt_value) == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("illegal sport: %s. should be integer\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
int tmp_val = atoi(opt_value);
|
||||
if (tmp_val <= 0 || tmp_val > 65535)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("illegal sport: %s. should be <1-65535>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.src_port = htons((unsigned short)tmp_val);
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_DPORT:
|
||||
{
|
||||
if (stm_string_isdigit(opt_value) == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("illegal sport: %s. should be integer\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
int tmp_val = atoi(opt_value);
|
||||
if (tmp_val <= 0 || tmp_val > 65535)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("illegal sport: %s. should be <1-65535>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.dst_port = htons((unsigned short)tmp_val);
|
||||
}
|
||||
break;
|
||||
|
||||
case SESSION_SCAN_CURSOR:
|
||||
brief_opt->scan_opt.cursor = strtoul(opt_value, NULL, 10);
|
||||
break;
|
||||
case SESSION_SCAN_COUNT:
|
||||
brief_opt->scan_opt.count = strtoul(opt_value, NULL, 10);
|
||||
if (brief_opt->scan_opt.count == 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("illegal count: %s. should be <1 - UINT32_MAX>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_LIMIT:
|
||||
brief_opt->limit = strtoul(opt_value, NULL, 10);
|
||||
if (brief_opt->limit == 0 || brief_opt->limit > SHOW_SESSION_BRIEF_LIMIT_MAX)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("illegal limit: %s. should be <1 - %u>\n", opt_value, SHOW_SESSION_BRIEF_LIMIT_MAX);
|
||||
goto error_exit;
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_CREATE_TIME:
|
||||
{
|
||||
time_t tmp_range[2] = {0, 0};
|
||||
if (stm_time_range_pton(opt_value, time(NULL), tmp_range) < 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("invalid create-time-in: %s. \r\nsyntax: <last-N-[seconds|minutes|hours|days]>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.create_time_ms[0] = tmp_range[0] * 1000; // second to ms
|
||||
brief_opt->scan_opt.create_time_ms[1] = tmp_range[1] * 1000; // second to ms
|
||||
}
|
||||
break;
|
||||
case SESSION_SCAN_LASPKT_TIME:
|
||||
{
|
||||
time_t tmp_range[2] = {0, 0};
|
||||
if (stm_time_range_pton(opt_value, time(NULL), tmp_range) < 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("invalid last-pkt-time-in: %s. \r\nsyntax: <last-N-[seconds|minutes|hours|days]>\n", opt_value);
|
||||
goto error_exit;
|
||||
}
|
||||
brief_opt->scan_opt.laspkt_time_ms[0] = tmp_range[0] * 1000; // second to ms
|
||||
brief_opt->scan_opt.laspkt_time_ms[1] = tmp_range[1] * 1000; // second to ms
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error_reply = monitor_reply_new_error("unrecognized params: %s \n", opt_name);
|
||||
return error_reply;
|
||||
}
|
||||
|
||||
if ((history_ipaddr_family != 0) && (ipaddr_family != 0) && (history_ipaddr_family != ipaddr_family))
|
||||
{
|
||||
error_reply = monitor_reply_new_error("contradictory ip version, expression rejects all sessions!\n");
|
||||
goto error_exit;
|
||||
}
|
||||
history_ipaddr_family = ipaddr_family;
|
||||
i++; // to next option
|
||||
brief_opt->scan_opt.flags |= flag;
|
||||
}
|
||||
ss = show_session_cli_args_sanity_check(brief_opt);
|
||||
if (ss && sdslen(ss) > 0)
|
||||
{
|
||||
error_reply = monitor_reply_new_error("%s\n", ss);
|
||||
sdsfree(ss);
|
||||
goto error_exit;
|
||||
}
|
||||
sdsfree(ss);
|
||||
error_reply = NULL;
|
||||
return NULL;
|
||||
|
||||
error_exit:
|
||||
return error_reply;
|
||||
}
|
||||
|
||||
static void get_single_session_brief(struct session *sess, int thread_id, struct show_session_brief_result *brief)
|
||||
{
|
||||
brief->thread_index = thread_id;
|
||||
brief->state = session_get_current_state(sess);
|
||||
brief->protocol = session_get_type(sess);
|
||||
session_is_symmetric(sess, &brief->flow_dir);
|
||||
brief->create_time_in_sec = session_get_timestamp(sess, SESSION_TIMESTAMP_START) / 1000; // ms to sec
|
||||
brief->last_pkt_time_in_sec = session_get_timestamp(sess, SESSION_TIMESTAMP_LAST) / 1000; // ms to sec
|
||||
const struct tuple6 *tp6 = session_get_tuple6(sess);
|
||||
memcpy(&brief->addr, tp6, sizeof(struct tuple6));
|
||||
}
|
||||
|
||||
struct iovec show_session_brief_on_worker_thread_rpc_cb(int thread_idx, struct iovec request, void *args)
|
||||
{
|
||||
struct iovec response = {};
|
||||
assert(request.iov_len == sizeof(struct stm_show_session_brief_opt));
|
||||
struct stm_show_session_brief_opt *show_brief_opt = (struct stm_show_session_brief_opt *)request.iov_base;
|
||||
|
||||
uint64_t session_id_array[SHOW_SESSION_BRIEF_LIMIT_MAX];
|
||||
uint64_t session_id_array_count = SHOW_SESSION_BRIEF_LIMIT_MAX;
|
||||
struct session_manager *sess_mgr = stellar_module_get_session_manager((struct stellar_module_manager *)args);
|
||||
struct session_manager_rte *sess_mgr_rt = session_manager_get_runtime(sess_mgr, thread_idx);
|
||||
session_id_array_count = session_manager_rte_scan_session(sess_mgr_rt, &show_brief_opt->scan_opt, session_id_array, show_brief_opt->limit);
|
||||
if (session_id_array_count == 0)
|
||||
{
|
||||
// no session match the filter params, but no error! still need to build a empty reply!
|
||||
// go on !!!
|
||||
response.iov_base = NULL;
|
||||
response.iov_len = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
struct show_session_brief_result *brief_result_array = (struct show_session_brief_result *)calloc(session_id_array_count, sizeof(struct show_session_brief_result));
|
||||
|
||||
for (uint32_t i = 0; i < session_id_array_count; i++)
|
||||
{
|
||||
struct session *sess = session_manager_rte_lookup_session_by_id(sess_mgr_rt, session_id_array[i]);
|
||||
assert(sess != NULL);
|
||||
get_single_session_brief(sess, thread_idx, &brief_result_array[i]);
|
||||
brief_result_array[i].sid = session_id_array[i];
|
||||
}
|
||||
response.iov_base = brief_result_array;
|
||||
response.iov_len = session_id_array_count;
|
||||
return response;
|
||||
}
|
||||
|
||||
static sds session_brief_to_readable(const struct show_session_brief_result *sess_brief)
|
||||
{
|
||||
char time_buf[64];
|
||||
char addr_buf[256];
|
||||
sds ss = sdsempty();
|
||||
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_brief->create_time_in_sec));
|
||||
ss = sdscatprintf(ss, "%-6d %-21lu %-5s %-8s %-20s %s", sess_brief->thread_index, sess_brief->sid,
|
||||
(sess_brief->protocol == SESSION_TYPE_TCP) ? "tcp" : "udp",
|
||||
stm_session_state_ntop(sess_brief->state), time_buf,
|
||||
stm_get0_readable_session_addr(&sess_brief->addr, addr_buf, sizeof(addr_buf)));
|
||||
return ss;
|
||||
}
|
||||
|
||||
static struct monitor_reply *show_session_brief_cmd_cb(struct stellar_monitor *monitor, int argc, char *argv[], void *arg)
|
||||
{
|
||||
struct stm_show_session_brief_opt brief_opt = {};
|
||||
if (show_session_is_help(argc, argv))
|
||||
{
|
||||
return show_session_brief_usage();
|
||||
}
|
||||
struct monitor_reply *error_reply = show_session_brief_cli_args_parse(argc, argv, &brief_opt);
|
||||
if (error_reply != NULL)
|
||||
{
|
||||
return error_reply;
|
||||
}
|
||||
struct monitor_reply *cmd_reply;
|
||||
struct stellar_module_manager *mod_mgr = (struct stellar_module_manager *)arg;
|
||||
int thread_num = stellar_module_manager_get_max_thread_num(mod_mgr);
|
||||
struct iovec request;
|
||||
request.iov_base = (void *)&brief_opt;
|
||||
request.iov_len = sizeof(struct stm_show_session_brief_opt);
|
||||
|
||||
struct iovec response[thread_num];
|
||||
memset(response, 0, sizeof(response));
|
||||
size_t tot_sess_id_num = 0;
|
||||
sds ss = sdsempty();
|
||||
ss = sdscatprintf(ss, "%-6s %-21s %-5s %-8s %-20s %s\r\n", "thread", "session-id", "proto", "state", "create-time", "tuple4(sip:sport-dip:dport)");
|
||||
ss = sdscatprintf(ss, "--------------------------------------------------------------------------------------------\r\n");
|
||||
|
||||
for (int i = 0; i < thread_num; i++)
|
||||
{
|
||||
response[i] = monitor_worker_thread_rpc(monitor, i, request, show_session_brief_on_worker_thread_rpc_cb, mod_mgr);
|
||||
if (response[i].iov_base == NULL || response[i].iov_len == 0) // empty result
|
||||
{
|
||||
continue;
|
||||
}
|
||||
struct show_session_brief_result *brief_res_array = (struct show_session_brief_result *)(response[i].iov_base);
|
||||
tot_sess_id_num += response[i].iov_len; // session_id_array_count
|
||||
for (size_t j = 0; j < response[i].iov_len; j++)
|
||||
{
|
||||
ss = sdscatprintf(ss, "%s\n", session_brief_to_readable(&brief_res_array[j]));
|
||||
}
|
||||
if (tot_sess_id_num >= brief_opt.limit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot_sess_id_num == 0)
|
||||
{
|
||||
cmd_reply = monitor_reply_new_string("No session found");
|
||||
goto empty_result;
|
||||
}
|
||||
cmd_reply = monitor_reply_new_string("%s", ss);
|
||||
|
||||
empty_result:
|
||||
sdsfree(ss);
|
||||
for (int i = 0; i < thread_num; i++)
|
||||
{
|
||||
if (response[i].iov_base)
|
||||
{
|
||||
free(response[i].iov_base);
|
||||
}
|
||||
}
|
||||
return cmd_reply;
|
||||
}
|
||||
|
||||
/*
|
||||
todo: add thread id,
|
||||
fast patch, avoid traversing all worker threads
|
||||
*/
|
||||
static struct monitor_reply *show_session_detail_cli_args_parse(int argc, char *argv[], struct stm_show_session_detail_opt *detail_opt)
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
return monitor_reply_new_error("missing session id\n");
|
||||
}
|
||||
if (argc > 5)
|
||||
{
|
||||
return monitor_reply_new_error("too many arguments\n");
|
||||
}
|
||||
if (strncasecmp(argv[3], "id", 2) != 0)
|
||||
{
|
||||
return monitor_reply_new_error("missing session id\n");
|
||||
}
|
||||
|
||||
if (stm_string_isdigit(argv[4]) == 0)
|
||||
{
|
||||
return monitor_reply_new_error("invalid session id: %s. should be integer in range: <1 - UINT64_MAX>\n", argv[4]);
|
||||
}
|
||||
detail_opt->sess_id = strtoull(argv[4], NULL, 10);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct iovec show_session_detail_on_worker_thread_rpc_cb(int thread_idx, struct iovec request, void *args)
|
||||
{
|
||||
struct iovec response = {};
|
||||
assert(request.iov_len == sizeof(struct stm_show_session_detail_opt));
|
||||
struct stm_show_session_detail_opt *detail_opt = (struct stm_show_session_detail_opt *)request.iov_base;
|
||||
|
||||
struct session_manager *sess_mgr = stellar_module_get_session_manager((struct stellar_module_manager *)args);
|
||||
struct session_manager_rte *sess_mgr_rt = session_manager_get_runtime(sess_mgr, thread_idx);
|
||||
|
||||
struct session *sess = session_manager_rte_lookup_session_by_id(sess_mgr_rt, detail_opt->sess_id);
|
||||
if (NULL == sess)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
struct show_session_detail_result *detail_res = (struct show_session_detail_result *)calloc(1, sizeof(struct show_session_detail_result));
|
||||
get_single_session_brief(sess, thread_idx, &detail_res->sess_brief);
|
||||
detail_res->sess_brief.sid = detail_opt->sess_id;
|
||||
detail_res->direction = session_get_direction(sess);
|
||||
// todo, get some exact stat, not all
|
||||
memcpy(detail_res->sess_stat, sess->stats, sizeof(detail_res->sess_stat));
|
||||
|
||||
// todo, get application info
|
||||
|
||||
response.iov_base = detail_res;
|
||||
response.iov_len = sizeof(struct show_session_detail_result);
|
||||
return response;
|
||||
}
|
||||
|
||||
static sds session_detail_to_readable(const struct show_session_detail_result *sess_detail)
|
||||
{
|
||||
char addr_buf[256];
|
||||
sds ss = sdsempty();
|
||||
char time_buf[64];
|
||||
#define SHOW_SESSION_DETAIL_NAME_FORMAT "%-30s"
|
||||
#define SHOW_SESSION_DETAIL_VALUE_FORMAT "%llu"
|
||||
const char *flow_str[MAX_FLOW_TYPE] = {"C2S flow", "S2C flow"};
|
||||
ss = sdscatprintf(ss, "%-15s: %lu\r\n", "session-id", sess_detail->sess_brief.sid);
|
||||
ss = sdscatprintf(ss, "%-15s: %d\r\n", "thread", sess_detail->sess_brief.thread_index);
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "state", stm_session_state_ntop(sess_detail->sess_brief.state));
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "protocol", (sess_detail->sess_brief.protocol == SESSION_TYPE_TCP) ? "tcp" : "udp");
|
||||
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_detail->sess_brief.create_time_in_sec));
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "create-time", time_buf);
|
||||
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_detail->sess_brief.last_pkt_time_in_sec));
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "last-pkt-time", time_buf);
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "tuple4", stm_get0_readable_session_addr(&sess_detail->sess_brief.addr, addr_buf, sizeof(addr_buf)));
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "symmetric", stm_session_flow_dir_ntop(sess_detail->sess_brief.flow_dir));
|
||||
ss = sdscatprintf(ss, "%-15s: %s\r\n", "direction", sess_detail->direction == SESSION_DIRECTION_INBOUND ? "INBOUND" : "OUTBOUND");
|
||||
|
||||
ss = sdscatprintf(ss, "statistics:\r\n");
|
||||
char printf_format[256];
|
||||
snprintf(printf_format, sizeof(printf_format), "\t%s : %s\r\n", SHOW_SESSION_DETAIL_NAME_FORMAT, SHOW_SESSION_DETAIL_VALUE_FORMAT);
|
||||
for (int flow = 0; flow < MAX_FLOW_TYPE; flow++)
|
||||
{
|
||||
ss = sdscatprintf(ss, " %s:\r\n", flow_str[flow]);
|
||||
ss = sdscatprintf(ss, printf_format, "raw_packets_received", sess_detail->sess_stat[flow][STAT_RAW_PACKETS_RECEIVED]);
|
||||
ss = sdscatprintf(ss, printf_format, "raw_bytes_received", sess_detail->sess_stat[flow][STAT_RAW_BYTES_RECEIVED]);
|
||||
ss = sdscatprintf(ss, printf_format, "duplicate_packets_bypass", sess_detail->sess_stat[flow][STAT_DUPLICATE_PACKETS_BYPASS]);
|
||||
ss = sdscatprintf(ss, printf_format, "injected_packets", sess_detail->sess_stat[flow][STAT_INJECTED_PACKETS_SUCCESS]);
|
||||
ss = sdscatprintf(ss, printf_format, "injected_bytes", sess_detail->sess_stat[flow][STAT_INJECTED_BYTES_SUCCESS]);
|
||||
if (SESSION_TYPE_TCP == sess_detail->sess_brief.protocol)
|
||||
{
|
||||
ss = sdscatprintf(ss, printf_format, "tcp_segments_retransmit", sess_detail->sess_stat[flow][STAT_TCP_SEGMENTS_RETRANSMIT]);
|
||||
ss = sdscatprintf(ss, printf_format, "tcp_payloads_retransmit", sess_detail->sess_stat[flow][STAT_TCP_PAYLOADS_RETRANSMIT]);
|
||||
ss = sdscatprintf(ss, printf_format, "tcp_segments_reordered", sess_detail->sess_stat[flow][STAT_TCP_SEGMENTS_REORDERED]);
|
||||
ss = sdscatprintf(ss, printf_format, "tcp_payloads_reordered", sess_detail->sess_stat[flow][STAT_TCP_PAYLOADS_REORDERED]);
|
||||
}
|
||||
}
|
||||
|
||||
// todo:
|
||||
ss = sdscatprintf(ss, "\r\n \033[31mtodo: add security policy rule id, HTTP URL, Server FQDN, etc... \033[0m");
|
||||
return ss;
|
||||
}
|
||||
|
||||
static struct monitor_reply *show_session_detail_cmd_cb(struct stellar_monitor *stm, int argc, char *argv[], void *arg)
|
||||
{
|
||||
struct stm_show_session_detail_opt detail_opt = {};
|
||||
detail_opt.thread_idx = -1;
|
||||
|
||||
if (show_session_is_help(argc, argv))
|
||||
{
|
||||
return show_session_detail_usage();
|
||||
}
|
||||
struct monitor_reply *error_reply = show_session_detail_cli_args_parse(argc, argv, &detail_opt);
|
||||
if (error_reply != NULL)
|
||||
{
|
||||
return error_reply;
|
||||
}
|
||||
|
||||
struct monitor_reply *cmd_reply;
|
||||
struct stellar_module_manager *mod_mgr = (struct stellar_module_manager *)arg;
|
||||
int thread_num = stellar_module_manager_get_max_thread_num(mod_mgr);
|
||||
struct iovec request;
|
||||
request.iov_base = (void *)&detail_opt;
|
||||
request.iov_len = sizeof(struct stm_show_session_detail_opt);
|
||||
|
||||
struct iovec response[thread_num];
|
||||
memset(response, 0, sizeof(response));
|
||||
size_t tot_sess_id_num = 0;
|
||||
sds ss = sdsempty();
|
||||
|
||||
for (int i = 0; i < thread_num; i++)
|
||||
{
|
||||
if (detail_opt.thread_idx != -1 && detail_opt.thread_idx != i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
response[i] = monitor_worker_thread_rpc(stm, i, request, show_session_detail_on_worker_thread_rpc_cb, mod_mgr);
|
||||
if (response[i].iov_base == NULL || response[i].iov_len == 0) // empty result
|
||||
{
|
||||
continue;
|
||||
}
|
||||
struct show_session_detail_result *detail_res = (struct show_session_detail_result *)(response[i].iov_base);
|
||||
ss = sdscatprintf(ss, "%s\n", session_detail_to_readable(detail_res));
|
||||
tot_sess_id_num++;
|
||||
break;
|
||||
}
|
||||
if (tot_sess_id_num == 0)
|
||||
{
|
||||
cmd_reply = monitor_reply_new_string("No session found by id %lu", detail_opt.sess_id);
|
||||
goto empty_result;
|
||||
}
|
||||
cmd_reply = monitor_reply_new_string("%s", ss);
|
||||
|
||||
empty_result:
|
||||
sdsfree(ss);
|
||||
for (int i = 0; i < thread_num; i++)
|
||||
{
|
||||
if (response[i].iov_base)
|
||||
{
|
||||
free(response[i].iov_base);
|
||||
}
|
||||
}
|
||||
return cmd_reply;
|
||||
}
|
||||
|
||||
int show_session_enforcer_init(struct stellar_module_manager *mod_mgr, struct stellar_monitor *stm)
|
||||
{
|
||||
monitor_register_cmd(stm, "show session brief", show_session_brief_cmd_cb, "readonly",
|
||||
"[sip | dip | sport | dport | protocol | state | cursor | count | limit | create-time-in | last-pkt-time-in ]",
|
||||
"Show session brief information", (void *)mod_mgr);
|
||||
monitor_register_cmd(stm, "show session detail", show_session_detail_cmd_cb, "readonly",
|
||||
"id <id>",
|
||||
"Show session verbose information", (void *)mod_mgr);
|
||||
return 0;
|
||||
}
|
||||
537
infra/monitor/monitor_cmd_assistant.c
Normal file
537
infra/monitor/monitor_cmd_assistant.c
Normal file
@@ -0,0 +1,537 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "cJSON.h"
|
||||
#include "sds/sds.h"
|
||||
#include "monitor_private.h"
|
||||
#include "monitor_cmd_assistant.h"
|
||||
#include "monitor_utils.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
struct stm_cmd_spec
|
||||
{
|
||||
const char *cmd_name;
|
||||
const char *cmd_flags;
|
||||
const char *cmd_hint;
|
||||
int cmd_arity;
|
||||
int cmd_first_key_offset;
|
||||
};
|
||||
|
||||
struct stm_cmd_assistant
|
||||
{
|
||||
cJSON *cjson_root;
|
||||
stm_cmd_assistant_completion_cb *cmd_completion_cb;
|
||||
stm_cmd_assistant_hints_cb *cmd_hints_cb;
|
||||
sds hints_result; // mallo and free for every hits
|
||||
};
|
||||
static __thread struct stm_cmd_assistant *__g_stm_cli_assistant = NULL;
|
||||
|
||||
static cJSON *stm_cli_register_cmd(cJSON *father_next_array, const char *cur_cmd_prefix)
|
||||
{
|
||||
cJSON *cur_level_item = NULL;
|
||||
|
||||
/* search current cmd (name is prefix) in father->next array[] */
|
||||
int array_size = cJSON_GetArraySize(father_next_array);
|
||||
for (int i = 0; i < array_size; i++)
|
||||
{
|
||||
cur_level_item = cJSON_GetArrayItem(father_next_array, i);
|
||||
cJSON *cmd_name_item = cJSON_GetObjectItem(cur_level_item, "prefix");
|
||||
if (cmd_name_item != NULL && (0 == strcmp(cur_cmd_prefix, cmd_name_item->valuestring)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_level_item = NULL;
|
||||
}
|
||||
}
|
||||
if (NULL == cur_level_item)
|
||||
{
|
||||
/* if not exist, create new cmd (name is prefix) */
|
||||
cur_level_item = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(cur_level_item, "prefix", cJSON_CreateString(cur_cmd_prefix));
|
||||
cJSON *new_cmd_next_array = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(cur_level_item, "next", new_cmd_next_array);
|
||||
|
||||
/* insert into father->next array */
|
||||
cJSON_AddItemToArray(father_next_array, cur_level_item);
|
||||
}
|
||||
else
|
||||
{
|
||||
; // already exist, do nothing
|
||||
}
|
||||
return cur_level_item;
|
||||
}
|
||||
|
||||
/*
|
||||
search json object by cli_cmd_line, if exsit , return 0, do nothing,
|
||||
the search json object function can be used for register_usage();
|
||||
if not exist, create new json object and insert into father->next array
|
||||
*/
|
||||
int stm_cmd_assistant_register(struct stm_cmd_assistant *aide, const char *cli_cmd_line)
|
||||
{
|
||||
int argc = 0;
|
||||
if (NULL == aide->cjson_root)
|
||||
{
|
||||
aide->cjson_root = cJSON_CreateArray();
|
||||
}
|
||||
sds *array = sdssplitargs(cli_cmd_line, &argc);
|
||||
cJSON *father_candidate_array = aide->cjson_root;
|
||||
cJSON *cur_cmd_obj = NULL;
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
cur_cmd_obj = stm_cli_register_cmd(father_candidate_array, array[i]);
|
||||
father_candidate_array = cJSON_GetObjectItem(cur_cmd_obj, "next");
|
||||
}
|
||||
sdsfreesplitres(array, argc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cJSON *stm_cli_search_cmd(cJSON *father_next_array, const char *cur_cmd_prefix)
|
||||
{
|
||||
cJSON *cur_level_item = NULL;
|
||||
/* search current cmd (name is prefix) in father->next array[] */
|
||||
int array_size = cJSON_GetArraySize(father_next_array);
|
||||
for (int i = 0; i < array_size; i++)
|
||||
{
|
||||
cur_level_item = cJSON_GetArrayItem(father_next_array, i);
|
||||
cJSON *cmd_name_item = cJSON_GetObjectItem(cur_level_item, "prefix");
|
||||
if (cmd_name_item != NULL && (0 == strcmp(cur_cmd_prefix, cmd_name_item->valuestring)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_level_item = NULL;
|
||||
}
|
||||
}
|
||||
return cur_level_item;
|
||||
}
|
||||
|
||||
cJSON *stm_cmd_assistant_search(struct stm_cmd_assistant *aide, const char *cli_cmd_line)
|
||||
{
|
||||
int argc = 0;
|
||||
sds *array = sdssplitargs(cli_cmd_line, &argc);
|
||||
cJSON *father_candidate_array = aide->cjson_root;
|
||||
cJSON *match_item = NULL;
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
match_item = stm_cli_search_cmd(father_candidate_array, array[i]);
|
||||
if (NULL == match_item)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
father_candidate_array = cJSON_GetObjectItem(match_item, "next");
|
||||
}
|
||||
sdsfreesplitres(array, argc);
|
||||
return match_item;
|
||||
}
|
||||
|
||||
int stm_cmd_assistant_register_usage(struct stm_cmd_assistant *aide, const char *cli_cmd_line, const char *usage)
|
||||
{
|
||||
if (NULL == aide || NULL == cli_cmd_line || NULL == usage)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (strlen(cli_cmd_line) == 0)
|
||||
{
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddItemToObjectCS(obj, "usage", cJSON_CreateString(usage));
|
||||
cJSON_AddItemToArray(aide->cjson_root, obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cJSON *obj = stm_cmd_assistant_search(aide, cli_cmd_line);
|
||||
if (NULL == obj)
|
||||
{
|
||||
stm_cmd_assistant_register(aide, cli_cmd_line);
|
||||
obj = stm_cmd_assistant_search(aide, cli_cmd_line);
|
||||
if (NULL == obj)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
cJSON_AddItemToObject(obj, "usage", cJSON_CreateString(usage));
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *stm_cmd_assistant_verbose_print(int format)
|
||||
{
|
||||
struct stm_cmd_assistant *aide = __g_stm_cli_assistant;
|
||||
if (NULL == aide->cjson_root)
|
||||
{
|
||||
return (char *)strdup("[]");
|
||||
}
|
||||
char *debug_print;
|
||||
if (format == 1)
|
||||
{
|
||||
debug_print = cJSON_Print(aide->cjson_root);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_print = cJSON_PrintUnformatted(aide->cjson_root);
|
||||
}
|
||||
return debug_print;
|
||||
}
|
||||
|
||||
char *stm_cmd_assistant_brief_print(void)
|
||||
{
|
||||
struct stm_cmd_assistant *aide = __g_stm_cli_assistant;
|
||||
sds s = sdsempty();
|
||||
s = sdscat(s, "Usage:\r\n");
|
||||
int array_size = cJSON_GetArraySize(aide->cjson_root);
|
||||
for (int sz = 0; sz < array_size; sz++)
|
||||
{
|
||||
cJSON *item = cJSON_GetArrayItem(aide->cjson_root, sz);
|
||||
cJSON *cmd_name_item = cJSON_GetObjectItem(item, "prefix");
|
||||
s = sdscatprintf(s, "\t%s\r\n", cmd_name_item->valuestring);
|
||||
}
|
||||
char *print_str = strdup(s);
|
||||
sdsfree(s);
|
||||
return print_str;
|
||||
}
|
||||
|
||||
int stm_cmd_assistant_json_load(struct stm_cmd_assistant *aide, const char *json_str)
|
||||
{
|
||||
if (NULL == aide || NULL == json_str || strlen(json_str) == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
cJSON *root = cJSON_Parse(json_str);
|
||||
if (NULL == root)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
aide->cjson_root = root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stm_cmd_assistant_free(struct stm_cmd_assistant *aide)
|
||||
{
|
||||
if (aide)
|
||||
{
|
||||
if (aide->cjson_root)
|
||||
{
|
||||
cJSON_Delete(aide->cjson_root);
|
||||
}
|
||||
if (aide->hints_result)
|
||||
{
|
||||
sdsfree(aide->hints_result);
|
||||
}
|
||||
free(aide);
|
||||
}
|
||||
}
|
||||
|
||||
struct stm_cmd_assistant *stm_cmd_assistant_new(void)
|
||||
{
|
||||
struct stm_cmd_assistant *aide = calloc(1, sizeof(struct stm_cmd_assistant));
|
||||
aide->cjson_root = NULL;
|
||||
aide->hints_result = NULL;
|
||||
__g_stm_cli_assistant = aide;
|
||||
return aide;
|
||||
}
|
||||
|
||||
struct stm_cmd_assistant *stm_cmd_assistant_get(void)
|
||||
{
|
||||
return __g_stm_cli_assistant;
|
||||
}
|
||||
|
||||
static int stm_cmd_exist(struct stm_cmd_assistant *aide, const char *cmd_name)
|
||||
{
|
||||
cJSON *root = aide->cjson_root;
|
||||
if (NULL == root)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int array_size = cJSON_GetArraySize(root);
|
||||
for (int sz = 0; sz < array_size; sz++)
|
||||
{
|
||||
cJSON *item = cJSON_GetArrayItem(root, sz);
|
||||
cJSON *cmd_name_item = cJSON_GetObjectItem(item, "prefix");
|
||||
if (cmd_name_item && cmd_name_item->valuestring)
|
||||
{
|
||||
if (strcasecmp(cmd_name, cmd_name_item->valuestring) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return value:
|
||||
* 0: success
|
||||
* -1: failed
|
||||
* 1: already exist
|
||||
*/
|
||||
int stm_cmd_assistant_register_cmd(struct stm_cmd_assistant *aide, const char *cmd_name, void *cmd_cb, void *cmd_arg,
|
||||
const char *flags, const char *hint, const char *desc)
|
||||
{
|
||||
if (NULL == aide || NULL == cmd_name || NULL == cmd_cb)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (stm_cmd_exist(aide, cmd_name))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (stm_strncasecmp_exactly(flags, "readonly", 8) != 0 && stm_strncasecmp_exactly(flags, "write", 5) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(obj, "prefix", cJSON_CreateString(cmd_name));
|
||||
cJSON_AddItemToObject(obj, "flags", cJSON_CreateString(flags));
|
||||
cJSON_AddItemToObject(obj, "hints", cJSON_CreateString(hint));
|
||||
cJSON_AddItemToObject(obj, "desc", cJSON_CreateString(desc));
|
||||
|
||||
char tmp_str[32] = {};
|
||||
snprintf(tmp_str, sizeof(tmp_str), "%p", cmd_cb);
|
||||
cJSON_AddItemToObject(obj, "cmd_cb", cJSON_CreateString(tmp_str));
|
||||
|
||||
snprintf(tmp_str, sizeof(tmp_str), "%p", cmd_arg);
|
||||
cJSON_AddItemToObject(obj, "cmd_arg", cJSON_CreateString(tmp_str));
|
||||
|
||||
if (NULL == aide->cjson_root)
|
||||
{
|
||||
aide->cjson_root = cJSON_CreateArray();
|
||||
}
|
||||
cJSON_AddItemToArray(aide->cjson_root, obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *stm_cmd_assistant_serialize(struct stm_cmd_assistant *aide)
|
||||
{
|
||||
if (NULL == aide)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
char *debug_print = cJSON_Print(aide->cjson_root);
|
||||
return debug_print;
|
||||
}
|
||||
|
||||
int stm_cmd_assistant_dserialize(struct stm_cmd_assistant *aide, const char *json)
|
||||
{
|
||||
cJSON *tmp_root = cJSON_Parse(json);
|
||||
if (NULL == tmp_root)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
aide->cjson_root = tmp_root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
return value:
|
||||
0: equal exactly, uesed for hints, or get_cmd_cb()
|
||||
-1: cli_array < register_cmd_array //raw command1 and command2 line has same section, and command1 line is shorter than command2, used for tab auto completion
|
||||
1: cli_array > register_cmd_array //raw command1 and command2 line has same section, and command1 line is longer than command2, used for get_cmd_cb()
|
||||
-2: not match any secions
|
||||
*/
|
||||
int stm_cmd_assistant_sds_compare(sds *cli_array, int cli_argc, sds *register_cmd_array, int register_argc)
|
||||
{
|
||||
if (0 == cli_argc || 0 == register_argc)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
// compare the first n-1 words, must be exactly match
|
||||
int min_argc = MIN(cli_argc, register_argc);
|
||||
for (int i = 0; i < min_argc - 1; i++)
|
||||
{
|
||||
// previous words must be exactly match, so use strcasecmp()
|
||||
if (strcasecmp(cli_array[i], register_cmd_array[i]) != 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
// compare the last common word use substring match
|
||||
if (strncasecmp(cli_array[min_argc - 1], register_cmd_array[min_argc - 1], strlen(cli_array[min_argc - 1])) == 0)
|
||||
{
|
||||
if (strcasecmp(cli_array[min_argc - 1], register_cmd_array[min_argc - 1]) == 0)
|
||||
{
|
||||
if (cli_argc == register_argc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (cli_argc < register_argc)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// cli command is not complete
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
int stm_cmd_assistant_set_completion_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_completion_cb *cb)
|
||||
{
|
||||
aide->cmd_completion_cb = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stm_cmd_assistant_set_hints_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_hints_cb *cb)
|
||||
{
|
||||
aide->cmd_hints_cb = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cjson_traversal_completion(struct stm_cmd_assistant *aide, sds *sds_cli_array, int cli_argc, cJSON *cjson_root, void *arg)
|
||||
{
|
||||
int array_size = cJSON_GetArraySize(cjson_root);
|
||||
for (int sz = 0; sz < array_size; sz++)
|
||||
{
|
||||
cJSON *array_item = cJSON_GetArrayItem(cjson_root, sz);
|
||||
cJSON *prefix_item = cJSON_GetObjectItem(array_item, "prefix");
|
||||
int register_cmd_section_num;
|
||||
sds *register_cmd_array = sdssplitargs(prefix_item->valuestring, ®ister_cmd_section_num);
|
||||
|
||||
int match = stm_cmd_assistant_sds_compare(sds_cli_array, cli_argc, register_cmd_array, register_cmd_section_num);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_section_num);
|
||||
if (match == 0 || match == -1)
|
||||
{
|
||||
aide->cmd_completion_cb(arg, prefix_item->valuestring);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void stm_cmd_assistant_completion_cb_fun(struct stm_cmd_assistant *aide, const char *buf, void *arg)
|
||||
{
|
||||
/* input cmd buf must <= registed command
|
||||
if buf_len < command_len ,auto completion the longest prefix
|
||||
if buf_len == command_len, add a blank space
|
||||
*/
|
||||
int argc = 0;
|
||||
sds *cli_cmd_array = sdssplitargs(buf, &argc);
|
||||
cjson_traversal_completion(aide, cli_cmd_array, argc, aide->cjson_root, arg);
|
||||
sdsfreesplitres(cli_cmd_array, argc);
|
||||
}
|
||||
|
||||
void stm_cmd_assistant_input_line(struct stm_cmd_assistant *aide, const char *line, void *arg)
|
||||
{
|
||||
stm_cmd_assistant_completion_cb_fun(aide, line, arg);
|
||||
}
|
||||
|
||||
static const char *cjson_traversal_hints(sds *sds_cli_array, int cli_argc, cJSON *cjson_root)
|
||||
{
|
||||
int array_size = cJSON_GetArraySize(cjson_root);
|
||||
for (int sz = 0; sz < array_size; sz++)
|
||||
{
|
||||
cJSON *array_item = cJSON_GetArrayItem(cjson_root, sz);
|
||||
cJSON *prefix_item = cJSON_GetObjectItem(array_item, "prefix");
|
||||
int register_cmd_section_num;
|
||||
sds *register_cmd_array = sdssplitargs(prefix_item->valuestring, ®ister_cmd_section_num);
|
||||
|
||||
int match = stm_cmd_assistant_sds_compare(sds_cli_array, cli_argc, register_cmd_array, register_cmd_section_num);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_section_num);
|
||||
if (match == 0) // hints must be exactly match
|
||||
{
|
||||
cJSON *hint_item = cJSON_GetObjectItem(array_item, "hints");
|
||||
if (hint_item)
|
||||
{
|
||||
return hint_item->valuestring;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *stm_cmd_assistant_input_line_for_hints(struct stm_cmd_assistant *aide, const char *line)
|
||||
{
|
||||
int argc = 0;
|
||||
sds *cli_cmd_array = sdssplitargs(line, &argc);
|
||||
const char *hints = cjson_traversal_hints(cli_cmd_array, argc, aide->cjson_root);
|
||||
sdsfreesplitres(cli_cmd_array, argc);
|
||||
return hints;
|
||||
}
|
||||
|
||||
static long long stm_cmd_assistant_get_item(struct stm_cmd_assistant *aide, const char *cmd_line, const char *json_item_name, const char *format)
|
||||
{
|
||||
long long item_value = 0;
|
||||
int array_size = cJSON_GetArraySize(aide->cjson_root);
|
||||
int cli_argc;
|
||||
sds *cli_cmd_array = sdssplitargs(cmd_line, &cli_argc);
|
||||
int last_match_cmd_section_num = -1;
|
||||
for (int sz = 0; sz < array_size; sz++)
|
||||
{
|
||||
cJSON *array_item = cJSON_GetArrayItem(aide->cjson_root, sz);
|
||||
cJSON *prefix_item = cJSON_GetObjectItem(array_item, "prefix");
|
||||
int register_cmd_section_num;
|
||||
sds *register_cmd_array = sdssplitargs(prefix_item->valuestring, ®ister_cmd_section_num);
|
||||
|
||||
int match = stm_cmd_assistant_sds_compare(cli_cmd_array, cli_argc, register_cmd_array, register_cmd_section_num);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_section_num);
|
||||
if (match >= 0)
|
||||
{
|
||||
cJSON *hint_item = cJSON_GetObjectItem(array_item, json_item_name);
|
||||
if (hint_item)
|
||||
{
|
||||
/* longest match */
|
||||
if (register_cmd_section_num > last_match_cmd_section_num)
|
||||
{
|
||||
last_match_cmd_section_num = register_cmd_section_num;
|
||||
sscanf(hint_item->valuestring, format, &item_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sdsfreesplitres(cli_cmd_array, cli_argc);
|
||||
return item_value;
|
||||
}
|
||||
|
||||
void *stm_cmd_assistant_get_cb(struct stm_cmd_assistant *aide, const char *cmd_line)
|
||||
{
|
||||
return (void *)stm_cmd_assistant_get_item(aide, cmd_line, "cmd_cb", "%p");
|
||||
}
|
||||
|
||||
void *stm_cmd_assistant_get_user_arg(struct stm_cmd_assistant *aide, const char *cmd_line)
|
||||
{
|
||||
return (void *)stm_cmd_assistant_get_item(aide, cmd_line, "cmd_arg", "%p");
|
||||
}
|
||||
|
||||
int stm_cmd_assistant_get_arity(struct stm_cmd_assistant *aide, const char *cmd_line)
|
||||
{
|
||||
return (int)stm_cmd_assistant_get_item(aide, cmd_line, "arity", "%d");
|
||||
}
|
||||
|
||||
sds stm_cmd_assistant_list_cmd_brief(struct stm_cmd_assistant *aide)
|
||||
{
|
||||
sds res = sdsempty();
|
||||
int array_size = cJSON_GetArraySize(aide->cjson_root);
|
||||
for (int sz = 0; sz < array_size; sz++)
|
||||
{
|
||||
cJSON *item = cJSON_GetArrayItem(aide->cjson_root, sz);
|
||||
cJSON *cmd_name_item = cJSON_GetObjectItem(item, "prefix");
|
||||
cJSON *cmd_desc_item = cJSON_GetObjectItem(item, "desc");
|
||||
res = sdscatprintf(res, "\"%s\", %s \r\n", cmd_name_item->valuestring, cmd_desc_item ? cmd_desc_item->valuestring : "");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
sds stm_cmd_assistant_list_cmd_verbose(struct stm_cmd_assistant *aide)
|
||||
{
|
||||
if (NULL == aide->cjson_root)
|
||||
{
|
||||
return sdsempty();
|
||||
}
|
||||
char *json_str = cJSON_PrintUnformatted(aide->cjson_root);
|
||||
sds res = sdsnew(json_str);
|
||||
free(json_str);
|
||||
return res;
|
||||
}
|
||||
60
infra/monitor/monitor_cmd_assistant.h
Normal file
60
infra/monitor/monitor_cmd_assistant.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "cJSON.h"
|
||||
#include "sds/sds.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
struct stm_cmd_assistant;
|
||||
struct stm_cmd_spec;
|
||||
|
||||
struct stm_cmd_assistant *stm_cmd_assistant_new();
|
||||
void stm_cmd_assistant_free(struct stm_cmd_assistant *aide);
|
||||
int stm_cmd_assistant_json_load(struct stm_cmd_assistant *aide, const char *json_str);
|
||||
char *stm_cmd_assistant_brief_print(void);
|
||||
char *stm_cmd_assistant_verbose_print(int format);
|
||||
int stm_cmd_assistant_is_help(const char *line);
|
||||
cJSON *stm_cmd_assistant_search(struct stm_cmd_assistant *aide, const char *cli_cmd_line);
|
||||
int stm_cmd_assistant_register(struct stm_cmd_assistant *aide, const char *cli_cmd_line);
|
||||
int stm_cmd_assistant_register_usage(struct stm_cmd_assistant *aide, const char *cli_cmd_line, const char *usage);
|
||||
|
||||
// return value shoule be free after uesd.
|
||||
char *stm_cmd_assistant_serialize(struct stm_cmd_assistant *aide);
|
||||
int stm_cmd_assistant_dserialize(struct stm_cmd_assistant *aide, const char *json);
|
||||
|
||||
/*
|
||||
* return value:
|
||||
* 0: success
|
||||
* -1: failed
|
||||
* 1: already exist
|
||||
*/
|
||||
int stm_cmd_assistant_register_cmd(struct stm_cmd_assistant *aide, const char *cmd, void *cmd_cb, void *cmd_arg,
|
||||
const char *flags, const char *hint, const char *description);
|
||||
|
||||
int stm_cmd_assistant_sds_compare(sds *cli_array, int cli_argc, sds *register_cmd_array, int register_argc);
|
||||
typedef void(stm_cmd_assistant_completion_cb)(void *arg, const char *candidate_completion);
|
||||
typedef char *(stm_cmd_assistant_hints_cb)(const char *line);
|
||||
int stm_cmd_assistant_set_completion_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_completion_cb *cb);
|
||||
int stm_cmd_assistant_set_hints_cb(struct stm_cmd_assistant *aide, stm_cmd_assistant_hints_cb *cb);
|
||||
void stm_cmd_assistant_input_line(struct stm_cmd_assistant *aide, const char *line, void *arg);
|
||||
const char *stm_cmd_assistant_input_line_for_hints(struct stm_cmd_assistant *aide, const char *line);
|
||||
struct stm_cmd_assistant *stm_cmd_assistant_get(void);
|
||||
|
||||
// struct stm_cmd_spec *stm_cmd_assistant_get_cmd(struct stm_cmd_assistant *aide, const char *cmd);
|
||||
void *stm_cmd_assistant_get_cb(struct stm_cmd_assistant *aide, const char *cmd);
|
||||
void *stm_cmd_assistant_get_user_arg(struct stm_cmd_assistant *aide, const char *cmd);
|
||||
int stm_cmd_assistant_get_arity(struct stm_cmd_assistant *aide, const char *cmd);
|
||||
sds stm_cmd_assistant_list_cmd_brief(struct stm_cmd_assistant *aide);
|
||||
sds stm_cmd_assistant_list_cmd_verbose(struct stm_cmd_assistant *aide);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
331
infra/monitor/monitor_private.h
Normal file
331
infra/monitor/monitor_private.h
Normal file
@@ -0,0 +1,331 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <pthread.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pcap/pcap.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "stellar/monitor.h"
|
||||
#include "sds/sds.h"
|
||||
|
||||
#include "stellar/module.h"
|
||||
#include "stellar/log.h"
|
||||
|
||||
#include <event2/event.h>
|
||||
#include <event2/listener.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/thread.h>
|
||||
#include <event2/http.h>
|
||||
#include "monitor_rpc.h"
|
||||
/********************************** limit definition *****************************************/
|
||||
|
||||
#ifndef STELLAR_MAX_THREAD_NUM
|
||||
#define STELLAR_MAX_THREAD_NUM (256)
|
||||
#endif
|
||||
#define STM_RINGBUF_SIZE (1024 * 1024) /* per thread */
|
||||
#define STM_CONNECTION_IDLE_TIMEOUT 300 /* How many seconds elapsed without input command, connection will closed */
|
||||
#define STM_REQUEST_TIMEOUT 5
|
||||
|
||||
#define STM_SERVER_LISTEN_IP "127.0.0.1"
|
||||
#define STM_SERVER_LISTEN_PORT 80
|
||||
#define STM_TZSP_UDP_PORT 37008 /* default port of TZSP protocol: https://en.wikipedia.org/wiki/TZSP# */
|
||||
|
||||
#define STM_SESSION_DEFAULT_SEARCH_COUNT 100 /* if no count params, max search session number */
|
||||
#define STM_SESSION_DEFAULT_LIMIT_NUM 10 /* if no limit params, max support result session number */
|
||||
#define STM_SESSION_MAX_LIMIT_NUM 1000
|
||||
|
||||
#define STM_UINT64_READABLE_STRING_MAX_LEN 21 /* MAX value is: 18446744073709551615 */
|
||||
#define STM_UINT32_READABLE_STRING_MAX_LEN 11 /* MAX value is: 4294967295 */
|
||||
|
||||
#define STM_CONNECTIVITY_DEFALUT_COUNT 5 /* ping default count */
|
||||
#define STM_CONNECTIVITY_DEFALUT_SIZE 64 /* ping default bytes */
|
||||
#define STM_CONNECTIVITY_MAX_COUNT 100 /* ping max count */
|
||||
#define STM_CONNECTIVITY_MAX_SIZE 65535 /* ping max bytes */
|
||||
|
||||
/************************************************************************/
|
||||
#define STM_CMD_CALLBACK_THREAD_LOCAL_MAGIC (0x1234ABCD)
|
||||
#define STM_RINGBUF_HDR_MAGIC (0x0ABCD12345678)
|
||||
|
||||
#define STM_RINGBUF_THREAD_IDX_SERVER 0
|
||||
#define STM_RINGBUF_THREAD_IDX_AGENT 1
|
||||
|
||||
#define STM_MONITOR_THREAD_ID 0 // There are only two threads, use fix id
|
||||
#define STM_WORKER_THREAD_ID 1 // There are only two threads, use fix id
|
||||
|
||||
#define STM_LOG_MODULE_NAME "monitor"
|
||||
#define STM_STAT_OUTPUT_PATH "log/monitor.fs4"
|
||||
#define STM_STAT_OUTPUT_INTERVAL_MS 3000
|
||||
#define STM_RESTFUL_VERSION "v1"
|
||||
#define STM_RESTFUL_RESOURCE "stellar_monitor"
|
||||
#define STM_RESTFUL_URI_CMD_KEY "raw_cmd" // example: http://127.0.0.1:80/v1/stellar_monitor?raw_cmd=show%20session
|
||||
#define STM_CLIENT_SERVER_SYNC_CMD "show command verbose"
|
||||
|
||||
#define STM_CLI_CMD_HINTS_COLOR 90
|
||||
#define STM_CLI_CMD_HINTS_BOLD 0
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG // release version
|
||||
#define STM_DBG_PRINT(fmt, args...)
|
||||
#else
|
||||
#define STM_DBG_PRINT(fmt, args...) fprintf(stderr, fmt, ##args)
|
||||
#endif
|
||||
|
||||
#ifndef CALLOC
|
||||
#define CALLOC(type, number) ((type *)calloc(sizeof(type), number))
|
||||
#endif
|
||||
|
||||
#ifndef FREE
|
||||
#define FREE(ptr) \
|
||||
{ \
|
||||
if (ptr) \
|
||||
{ \
|
||||
free((void *)ptr); \
|
||||
ptr = NULL; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define STM_TIME_START() \
|
||||
struct timespec __start_time, __end_time; \
|
||||
unsigned long long diff; \
|
||||
clock_gettime(CLOCK_MONOTONIC, &__start_time);
|
||||
|
||||
#define STM_TIME_DIFF() \
|
||||
{ \
|
||||
clock_gettime(CLOCK_MONOTONIC, &__end_time); \
|
||||
if (__start_time.tv_sec == __end_time.tv_sec) \
|
||||
{ \
|
||||
diff = (unsigned long long)(__end_time.tv_nsec - __start_time.tv_nsec); \
|
||||
} \
|
||||
diff = ((unsigned long long)__end_time.tv_sec * 1000 * 1000 * 1000 + __end_time.tv_nsec) - ((unsigned long long)__start_time.tv_sec * 1000 * 1000 * 1000 + __start_time.tv_nsec); \
|
||||
}
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#define STM_LOG_DEBUG(format, ...) STELLAR_LOG_DEBUG(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
|
||||
#define STM_LOG_INFO(format, ...) STELLAR_LOG_INFO(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
|
||||
#define STM_LOG_ERROR(format, ...) STELLAR_LOG_ERROR(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
|
||||
#define STM_LOG_FATAL(format, ...) STELLAR_LOG_FATAL(__thread_local_logger, STM_LOG_MODULE_NAME, format, ##__VA_ARGS__)
|
||||
|
||||
enum stm_http_response_code
|
||||
{
|
||||
STM_HTTP_200_OK = 200,
|
||||
STM_HTTP_204_NO_CONTENT = 204,
|
||||
STM_HTTP_403_FORBIDDEN = 403,
|
||||
STM_HTTP_408_REQUEST_TIMEOUT = 408,
|
||||
STM_HTTP_413_PAYLOAD_TOO_LARGE = 413,
|
||||
};
|
||||
|
||||
enum stm_stat_type
|
||||
{
|
||||
STM_STAT_CLI_CONNECTION_NEW,
|
||||
STM_STAT_CLI_CONNECTION_CLOSE,
|
||||
STM_STAT_CLI_REQUEST_SUCC,
|
||||
STM_STAT_CLI_RESPONSE_SUCC,
|
||||
STM_STAT_CLI_REQUEST_ERR, // RESTFul Syntax error!
|
||||
STM_STAT_CLI_RESPONSE_ERR, // attention: empty result is not error!
|
||||
STM_STAT_MAX,
|
||||
};
|
||||
|
||||
struct stm_spinlock;
|
||||
|
||||
struct stellar_monitor_config
|
||||
{
|
||||
// int thread_count;
|
||||
size_t ringbuf_size; /* bytes */
|
||||
int connection_idle_timeout;
|
||||
int cli_request_timeout;
|
||||
char *listen_ipaddr;
|
||||
unsigned short listen_port_host_order;
|
||||
unsigned short data_link_bind_port_host_order; // for TZSP protocol
|
||||
int output_interval_ms;
|
||||
char *output_path;
|
||||
};
|
||||
|
||||
struct stm_key_value_tuple
|
||||
{
|
||||
char *key;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct stm_key_value
|
||||
{
|
||||
int tuple_num;
|
||||
struct stm_key_value_tuple *tuple;
|
||||
};
|
||||
|
||||
typedef struct evhttp_request stm_network_connection;
|
||||
|
||||
struct stm_cmd_transaction
|
||||
{
|
||||
struct stm_cmd_request *cmd_req;
|
||||
struct stm_cmd_reply *cmd_res[STELLAR_MAX_THREAD_NUM]; // multi thread merge to one
|
||||
};
|
||||
|
||||
struct stm_connection_manager
|
||||
{
|
||||
struct timeval link_start_time;
|
||||
struct timeval last_active_time;
|
||||
struct evhttp_connection *conn;
|
||||
char peer_ipaddr[INET6_ADDRSTRLEN];
|
||||
uint16_t peer_port_host_order;
|
||||
struct stm_connection_manager *next, *prev;
|
||||
};
|
||||
|
||||
struct stm_stat_counter
|
||||
{
|
||||
int counter_id;
|
||||
uint64_t count;
|
||||
uint64_t bytes;
|
||||
};
|
||||
|
||||
struct stm_stat
|
||||
{
|
||||
void *fs4_ins;
|
||||
struct stm_stat_counter counters[STM_STAT_MAX];
|
||||
};
|
||||
|
||||
struct monitor_connection
|
||||
{
|
||||
struct evhttp_connection *current_evconn_ref;
|
||||
};
|
||||
|
||||
/* optional API */
|
||||
struct monitor_connection;
|
||||
typedef void(monitor_connection_close_cb)(struct monitor_connection *conn, void *arg);
|
||||
int monitor_register_connection_close_cb(struct stellar_monitor *monitor, monitor_connection_close_cb *cb, void *arg);
|
||||
struct stm_conn_close_cb_manager
|
||||
{
|
||||
monitor_connection_close_cb *cb;
|
||||
void *arg;
|
||||
struct stm_conn_close_cb_manager *next, *prev;
|
||||
};
|
||||
|
||||
struct stm_pktdump_runtime;
|
||||
struct stellar_monitor
|
||||
{
|
||||
struct module_manager *mod_mgr_ref;
|
||||
struct logger *logger_ref;
|
||||
int worker_thread_num;
|
||||
struct stellar_monitor_config *config;
|
||||
struct stm_cmd_assistant *aide; // reference, share with stellar
|
||||
struct stm_connection_manager *connection_mgr; // used to tracking all connections, for cli "who" command
|
||||
struct stm_conn_close_cb_manager *conn_close_mgr;
|
||||
// struct stm_ringbuf_mgr *ringbuf_mgr[STELLAR_MAX_THREAD_NUM];
|
||||
struct event_base *evt_base;
|
||||
// struct event *ev_timeout;
|
||||
struct evhttp *evt_http_server;
|
||||
pthread_t evt_main_loop_tid;
|
||||
struct timeval time_now;
|
||||
struct stm_stat *stat;
|
||||
struct stm_spinlock *lock; // for dynamic register command, conn_close_cb
|
||||
int (*gettime_cb)(struct timeval *tv, struct timezone *tz);
|
||||
struct monitor_connection current_conn;
|
||||
struct stm_pktdump_runtime *packet_dump;
|
||||
|
||||
struct monitor_rpc **rpc_ins_array; // multir threads
|
||||
};
|
||||
|
||||
enum monitor_reply_type
|
||||
{
|
||||
MONITOR_REPLY_INTEGER,
|
||||
MONITOR_REPLY_DOUBLE,
|
||||
MONITOR_REPLY_STRING,
|
||||
MONITOR_REPLY_ERROR,
|
||||
MONITOR_REPLY_STATUS,
|
||||
MONITOR_REPLY_NIL,
|
||||
};
|
||||
|
||||
struct monitor_reply
|
||||
{
|
||||
enum monitor_reply_type type;
|
||||
long long integer; /* The integer when type is SWARMKV_REPLY_INTEGER */
|
||||
double dval; /* The double when type is SWARMKV_REPLY_DOUBLE */
|
||||
int len; /* Length of string */
|
||||
char *str;
|
||||
int http_code;
|
||||
const char *http_reason;
|
||||
};
|
||||
|
||||
struct monitor_cli_args
|
||||
{
|
||||
const char *short_opt;
|
||||
const char *long_opt;
|
||||
int require_arg_value;
|
||||
int value_is_multi_words; // "a b c d e f g"
|
||||
char *value; // should be free after use
|
||||
};
|
||||
/************************************************************************************************************/
|
||||
/* monitor call gettimeofday(2) by default */
|
||||
struct stellar_monitor_config *stellar_monitor_config_new(const char *toml);
|
||||
int stellar_monitor_set_gettime_callback(struct stellar_monitor *stm, int (*gettime_cb)(struct timeval *tv, struct timezone *tz));
|
||||
struct stellar_monitor *stellar_monitor_get(void);
|
||||
struct stm_connection_manager *stm_connection_insert(struct evhttp_connection *evconn);
|
||||
void stm_connection_update(struct stm_connection_manager *conn_mgr, const struct evhttp_connection *evconn);
|
||||
void stm_connection_delete(struct evhttp_connection *evconn);
|
||||
const struct stm_connection_manager *stm_connection_search(const struct stm_connection_manager *conn_mgr_head, const struct evhttp_connection *evconn);
|
||||
|
||||
struct stm_key_value *stm_cmd_key_value_new(void);
|
||||
void stm_cmd_key_value_append(struct stm_key_value **kv, const char *key, const char *value);
|
||||
void stm_cmd_key_value_free(struct stm_key_value *kv);
|
||||
|
||||
/************************************** command manager **********************************************/
|
||||
struct stm_stat *stm_stat_init(struct stellar_monitor *stm);
|
||||
sds stm_config_print(const struct stellar_monitor_config *config);
|
||||
void stm_stat_free(struct stm_stat *stat);
|
||||
void stm_stat_update(struct stm_stat *stat, int thread_idx, enum stm_stat_type type, long long value);
|
||||
long long stm_get_stat_count(struct stm_stat *stat, enum stm_stat_type type);
|
||||
long long stm_get_stat_bytes(struct stm_stat *stat, enum stm_stat_type type);
|
||||
sds monitor_reply_to_string(const struct monitor_reply *reply);
|
||||
void monitor_reply_free(struct monitor_reply *reply);
|
||||
int monitor_util_parse_cmd_args(int argc, const char *argv[], struct monitor_cli_args cli_args[], size_t cli_args_array_size);
|
||||
char *stm_http_url_encode(const char *originalText);
|
||||
struct stm_spinlock *stm_spinlock_new(void);
|
||||
void stm_spinlock_lock(struct stm_spinlock *splock);
|
||||
void stm_spinlock_unlock(struct stm_spinlock *splock);
|
||||
void stm_spinlock_free(struct stm_spinlock *splock);
|
||||
struct stm_pktdump_runtime *stm_packet_dump_new(struct stellar_monitor *stm, const struct stellar_monitor_config *config);
|
||||
void stm_pktdump_enforcer_free(struct stellar_monitor *stm);
|
||||
|
||||
struct monitor_rpc *stm_rpc_new(void);
|
||||
void stm_rpc_free(struct monitor_rpc *rpc_ins);
|
||||
int stm_rpc_exec(int thread_idx, struct monitor_rpc *rpc_ins);
|
||||
struct iovec stm_rpc_call(struct monitor_rpc *rpc_ins, struct iovec rpc_request, monitor_rpc_callabck *cb, void *user_args);
|
||||
void monitor_rpc_free(struct monitor_rpc *rpc_ins);
|
||||
struct monitor_rpc *monitor_rpc_new(struct stellar_monitor *stm, struct module_manager *mod_mgr);
|
||||
struct stellar_monitor *monitor_new(const char *toml_file, struct module_manager *mod_mgr, struct logger *logh);
|
||||
|
||||
/* Must be called in 'monitor_cmd_cb' context */
|
||||
struct monitor_connection *monitor_get_current_connection(struct stellar_monitor *monitor);
|
||||
/* Get the remote address and port associated with this connection. */
|
||||
int monitor_get_peer_addr(struct monitor_connection *conn, char **peer_ip, unsigned short *peer_port);
|
||||
|
||||
/* command enforcer */
|
||||
int show_session_enforcer_init(struct module_manager *mod_mgr, struct stellar_monitor *stm);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
137
infra/monitor/monitor_ringbuf.c
Normal file
137
infra/monitor/monitor_ringbuf.c
Normal file
@@ -0,0 +1,137 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include "monitor_private.h"
|
||||
#include "monitor_ringbuf.h"
|
||||
|
||||
struct monitor_ringbuf_wrap
|
||||
{
|
||||
ringbuf_t *ringbuf;
|
||||
char *ringbuf_data;
|
||||
unsigned long long push_number; // only for statistics
|
||||
unsigned long long push_bytes; // only for statistics
|
||||
unsigned long long pop_number; // only for statistics
|
||||
unsigned long long pop_bytes; // only for statistics
|
||||
};
|
||||
|
||||
ssize_t stm_ringbuf_stream_start(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t require_size)
|
||||
{
|
||||
ringbuf_worker_t *rb_worker = ringbuf_register(rbf->ringbuf, thread_id);
|
||||
ssize_t offset = ringbuf_acquire(rbf->ringbuf, rb_worker, require_size);
|
||||
if (offset < 0)
|
||||
{
|
||||
STM_DBG_PRINT("stm ringbuf stream prealloc buffer(): ringbuf_acquire fail, no valid space!\n");
|
||||
return 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
int stm_ringbuf_stream_append(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t rbf_offset, const void *value, size_t len)
|
||||
{
|
||||
(void)thread_id;
|
||||
memcpy(rbf->ringbuf_data + rbf_offset, value, len);
|
||||
rbf->push_number++;
|
||||
rbf->push_bytes += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stm_ringbuf_stream_finish(int thread_id, struct monitor_ringbuf_wrap *rbf)
|
||||
{
|
||||
ringbuf_worker_t *rb_worker = ringbuf_register(rbf->ringbuf, thread_id);
|
||||
ringbuf_produce(rbf->ringbuf, rb_worker);
|
||||
}
|
||||
|
||||
int stm_ringbuf_easy_push(int thread_id, struct monitor_ringbuf_wrap *rbf, const void *push_value /*must continuous*/, size_t push_len)
|
||||
{
|
||||
ringbuf_worker_t *rb_worker = ringbuf_register(rbf->ringbuf, thread_id);
|
||||
ssize_t offset = ringbuf_acquire(rbf->ringbuf, rb_worker, push_len);
|
||||
if (offset < 0)
|
||||
{
|
||||
STM_DBG_PRINT("stm ringbuf easy push(): ringbuf_acquire fail, no valid space!\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(rbf->ringbuf_data + offset, push_value, push_len);
|
||||
|
||||
ringbuf_produce(rbf->ringbuf, rb_worker);
|
||||
rbf->push_number++;
|
||||
rbf->push_bytes += push_len;
|
||||
// STM_DBG_PRINT("stm ringbuf push() success, len:%llu, number:%llu\n", push_len, rbf->push_number);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *stm_ringbuf_pop(struct monitor_ringbuf_wrap *rbf, size_t *pop_len)
|
||||
{
|
||||
size_t len = 0, offset = 0;
|
||||
len = ringbuf_consume(rbf->ringbuf, &offset);
|
||||
if (0 == len)
|
||||
{
|
||||
// STM_DBG_PRINT("stm_ringbuf_pop(): not valid data\n");
|
||||
*pop_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
rbf->pop_number++;
|
||||
*pop_len = len;
|
||||
// STM_DBG_PRINT("stm_ringbuf_pop() success, len:%llu, number:%llu\n", len, rbf->pop_number);
|
||||
return rbf->ringbuf_data + offset;
|
||||
}
|
||||
|
||||
void stm_ringbuf_release(struct monitor_ringbuf_wrap *rbf, int rel_len)
|
||||
{
|
||||
ringbuf_release(rbf->ringbuf, rel_len);
|
||||
rbf->pop_bytes += rel_len;
|
||||
}
|
||||
|
||||
struct monitor_ringbuf_wrap *stm_ringbuf_wrap_new(int thread_tot_num, size_t ringbuf_size)
|
||||
{
|
||||
struct monitor_ringbuf_wrap *rbf = (struct monitor_ringbuf_wrap *)calloc(1, sizeof(struct monitor_ringbuf_wrap));
|
||||
size_t ringbuf_obj_size;
|
||||
ringbuf_get_sizes(thread_tot_num, &ringbuf_obj_size, NULL);
|
||||
rbf->ringbuf = (ringbuf_t *)calloc(1, ringbuf_obj_size);
|
||||
rbf->ringbuf_data = (char *)calloc(1, ringbuf_size);
|
||||
ringbuf_setup(rbf->ringbuf, thread_tot_num, ringbuf_size);
|
||||
return rbf;
|
||||
}
|
||||
|
||||
void stm_ringbuf_wrap_free(struct monitor_ringbuf_wrap *rbf)
|
||||
{
|
||||
if (NULL == rbf)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (rbf->ringbuf)
|
||||
{
|
||||
free(rbf->ringbuf);
|
||||
}
|
||||
if (rbf->ringbuf_data)
|
||||
{
|
||||
free(rbf->ringbuf_data);
|
||||
}
|
||||
free(rbf);
|
||||
}
|
||||
|
||||
void stm_ringbuf_get_statistics(const struct monitor_ringbuf_wrap *rbf, unsigned long long *push_number, unsigned long long *push_bytes, unsigned long long *pop_number, unsigned long long *pop_bytes)
|
||||
{
|
||||
if (NULL == rbf)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (push_number)
|
||||
{
|
||||
*push_number = rbf->push_number;
|
||||
}
|
||||
if (push_bytes)
|
||||
{
|
||||
*push_bytes = rbf->push_bytes;
|
||||
}
|
||||
if (pop_number)
|
||||
{
|
||||
*pop_number = rbf->pop_number;
|
||||
}
|
||||
if (pop_bytes)
|
||||
{
|
||||
*pop_bytes = rbf->pop_bytes;
|
||||
}
|
||||
}
|
||||
30
infra/monitor/monitor_ringbuf.h
Normal file
30
infra/monitor/monitor_ringbuf.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "ringbuf/ringbuf.h"
|
||||
|
||||
/*
|
||||
Message format:
|
||||
|| header | payload ....(variable-length) || header | payload ....(variable-length) || ....
|
||||
*/
|
||||
|
||||
struct monitor_ringbuf_wrap;
|
||||
struct monitor_ringbuf_wrap *stm_ringbuf_wrap_new(int thread_tot_num, size_t ringbuf_size);
|
||||
int stm_ringbuf_easy_push(int thread_id, struct monitor_ringbuf_wrap *rbf, const void *value, size_t len);
|
||||
void stm_ringbuf_release(struct monitor_ringbuf_wrap *rbf, int rel_len);
|
||||
void *stm_ringbuf_pop(struct monitor_ringbuf_wrap *rbf, size_t *pop_len);
|
||||
void stm_ringbuf_wrap_free(struct monitor_ringbuf_wrap *rbf);
|
||||
ssize_t stm_ringbuf_stream_start(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t require_size);
|
||||
int stm_ringbuf_stream_append(int thread_id, struct monitor_ringbuf_wrap *rbf, size_t rbf_offset, const void *value, size_t len);
|
||||
void stm_ringbuf_stream_finish(int thread_id, struct monitor_ringbuf_wrap *rbf);
|
||||
void stm_ringbuf_get_statistics(const struct monitor_ringbuf_wrap *rbf, unsigned long long *push_number,
|
||||
unsigned long long *push_bytes, unsigned long long *pop_number, unsigned long long *pop_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
115
infra/monitor/monitor_rpc.c
Normal file
115
infra/monitor/monitor_rpc.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <stddef.h>
|
||||
#include "stellar/monitor.h"
|
||||
#include "monitor_private.h"
|
||||
#include "monitor_rpc.h"
|
||||
#include "stellar/module.h"
|
||||
|
||||
#define RPC_WORKER_THREAD_BUSY 1
|
||||
#define RPC_WORKER_THREAD_IDLE 0
|
||||
|
||||
struct monitor_rpc_msg_hdr
|
||||
{
|
||||
unsigned int type;
|
||||
unsigned int length; // total messaage length, include this header, = payload length + sizeof(struct monitor_rpc_msg_hdr)
|
||||
char value[0]; // variable-length, continuous
|
||||
} __attribute__((packed));
|
||||
|
||||
enum monitor_rpc_ringbuf_dir
|
||||
{ // full duplex, dir: 0: worker thread to monitor thread; 1: monitor thread to worker thread
|
||||
RPC_RINBUG_DIR_W2M = 0,
|
||||
RPC_RINBUG_DIR_M2W = 1,
|
||||
RPC_RINBUG_DIR_MAX = 2,
|
||||
};
|
||||
|
||||
struct monitor_rpc
|
||||
{
|
||||
volatile long atomic_val;
|
||||
|
||||
monitor_rpc_callabck *rpc_cb;
|
||||
void *rpc_args;
|
||||
struct iovec rpc_request;
|
||||
struct iovec rpc_response;
|
||||
};
|
||||
|
||||
struct iovec stm_rpc_call(struct monitor_rpc *rpc_ins, struct iovec rpc_request, monitor_rpc_callabck *cb, void *user_args)
|
||||
{
|
||||
while (__sync_or_and_fetch(&rpc_ins->atomic_val, 0) == RPC_WORKER_THREAD_BUSY)
|
||||
{
|
||||
// wait for the last rpc response, not support concurrent rpc yet!
|
||||
usleep(1000);
|
||||
}
|
||||
rpc_ins->rpc_cb = cb;
|
||||
rpc_ins->rpc_args = user_args;
|
||||
rpc_ins->rpc_request = rpc_request;
|
||||
__sync_fetch_and_or(&rpc_ins->atomic_val, 1);
|
||||
|
||||
while (__sync_or_and_fetch(&rpc_ins->atomic_val, 0) == RPC_WORKER_THREAD_BUSY)
|
||||
{
|
||||
// wait for the rpc response...
|
||||
usleep(1000);
|
||||
}
|
||||
return rpc_ins->rpc_response;
|
||||
}
|
||||
|
||||
int stm_rpc_exec(int thread_idx, struct monitor_rpc *rpc_ins)
|
||||
{
|
||||
if (0 == __sync_or_and_fetch(&rpc_ins->atomic_val, RPC_WORKER_THREAD_IDLE))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rpc_ins->rpc_response = rpc_ins->rpc_cb(thread_idx, rpc_ins->rpc_request, rpc_ins->rpc_args);
|
||||
__sync_fetch_and_and(&rpc_ins->atomic_val, RPC_WORKER_THREAD_IDLE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Communicate between different threads by ringbuf.
|
||||
*/
|
||||
struct iovec monitor_worker_thread_rpc(struct stellar_monitor *stm, int worker_thread_idx, struct iovec rpc_request, monitor_rpc_callabck *cb, void *user_args)
|
||||
{
|
||||
int worker_thread_num = module_manager_get_max_thread_num(stm->mod_mgr_ref);
|
||||
if (worker_thread_idx >= worker_thread_num)
|
||||
{
|
||||
struct iovec response = {0};
|
||||
return response;
|
||||
}
|
||||
struct monitor_rpc *rpc_ins = stm->rpc_ins_array[worker_thread_idx];
|
||||
return stm_rpc_call(rpc_ins, rpc_request, cb, user_args);
|
||||
}
|
||||
|
||||
__thread long long rpc_idle_num = 0;
|
||||
|
||||
void module_rpc_worker_thread_polling_cb(struct module_manager *mod_mgr, void *polling_arg)
|
||||
{
|
||||
struct stellar_monitor *stm = (struct stellar_monitor *)polling_arg;
|
||||
int thread_idx = module_manager_get_thread_id(mod_mgr);
|
||||
struct monitor_rpc *rpc_ins = stm->rpc_ins_array[thread_idx];
|
||||
|
||||
stm_rpc_exec(thread_idx, rpc_ins);
|
||||
}
|
||||
|
||||
struct monitor_rpc *stm_rpc_new(void)
|
||||
{
|
||||
struct monitor_rpc *rpc_ins = (struct monitor_rpc *)calloc(1, sizeof(struct monitor_rpc));
|
||||
return rpc_ins;
|
||||
}
|
||||
|
||||
void stm_rpc_free(struct monitor_rpc *rpc_ins)
|
||||
{
|
||||
if (NULL == rpc_ins)
|
||||
{
|
||||
return;
|
||||
}
|
||||
free(rpc_ins);
|
||||
}
|
||||
|
||||
struct monitor_rpc *monitor_rpc_new(struct stellar_monitor *stm, struct module_manager *mod_mgr)
|
||||
{
|
||||
module_manager_polling_subscribe(mod_mgr, module_rpc_worker_thread_polling_cb, (void *)stm);
|
||||
return stm_rpc_new();
|
||||
}
|
||||
|
||||
void monitor_rpc_free(struct monitor_rpc *rpc_ins)
|
||||
{
|
||||
stm_rpc_free(rpc_ins);
|
||||
}
|
||||
10
infra/monitor/monitor_rpc.h
Normal file
10
infra/monitor/monitor_rpc.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "stellar/monitor.h"
|
||||
#include <bits/types/struct_iovec.h>
|
||||
|
||||
struct monitor_rpc;
|
||||
|
||||
typedef struct iovec(monitor_rpc_callabck)(int worker_thread_idx, struct iovec user_data, void *user_args);
|
||||
struct iovec monitor_worker_thread_rpc(struct stellar_monitor *monitor, int worker_thread_idx, struct iovec user_data, monitor_rpc_callabck *cb, void *user_args);
|
||||
591
infra/monitor/monitor_server.c
Normal file
591
infra/monitor/monitor_server.c
Normal file
@@ -0,0 +1,591 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <evhttp.h>
|
||||
#include <event2/event.h>
|
||||
#include <event2/keyvalq_struct.h>
|
||||
#include <sys/queue.h>
|
||||
#include "stellar/log.h"
|
||||
#include "monitor_private.h"
|
||||
#include "monitor_cmd_assistant.h"
|
||||
#include "monitor/monitor_utils.h"
|
||||
#include "toml/toml.h"
|
||||
#include "sds/sds.h"
|
||||
#include "uthash/utlist.h"
|
||||
|
||||
static __thread struct stellar_monitor *__thread_local_stm;
|
||||
static __thread pthread_t __thread_local_tid;
|
||||
static __thread pthread_t __stm_libevevt_callback_thread_local_tid;
|
||||
static __thread struct logger *__stm_thread_local_logger = NULL;
|
||||
|
||||
static void stm_save_thread_local_context(struct stellar_monitor *stm)
|
||||
{
|
||||
__thread_local_stm = stm;
|
||||
__thread_local_tid = pthread_self();
|
||||
__stm_thread_local_logger = stm->logger_ref;
|
||||
}
|
||||
|
||||
static void stm_connection_close_notify(struct stellar_monitor *stm, UNUSED struct evhttp_connection *evconn)
|
||||
{
|
||||
struct stm_conn_close_cb_manager *ele, *tmp;
|
||||
DL_FOREACH_SAFE(stm->conn_close_mgr, ele, tmp)
|
||||
{
|
||||
ele->cb(&stm->current_conn, ele->arg);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_connection_close_cb(UNUSED struct evhttp_connection *ev_conn, UNUSED void *arg)
|
||||
{
|
||||
__stm_libevevt_callback_thread_local_tid = pthread_self();
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
stm->current_conn.current_evconn_ref = ev_conn;
|
||||
stm_spinlock_lock(stm->lock);
|
||||
stm_connection_delete(ev_conn);
|
||||
stm_connection_close_notify(stm, ev_conn);
|
||||
stm_spinlock_unlock(stm->lock);
|
||||
char *peer_ip_addr;
|
||||
uint16_t peer_port;
|
||||
evhttp_connection_get_peer(ev_conn, &peer_ip_addr, &peer_port);
|
||||
STELLAR_LOG_INFO(stm->logger_ref, STM_LOG_MODULE_NAME, "cli connection closed, client %s:%u\n", peer_ip_addr, peer_port);
|
||||
stm_stat_update(stm->stat, stm->worker_thread_num, STM_STAT_CLI_CONNECTION_CLOSE, 1);
|
||||
stm->current_conn.current_evconn_ref = NULL;
|
||||
}
|
||||
|
||||
static void stm_command_send_reply_by_cstr(struct evhttp_request *request, int http_status_code, const char *reply, UNUSED void *params)
|
||||
{
|
||||
struct evbuffer *buffer = evbuffer_new();
|
||||
evbuffer_add(buffer, reply, strlen(reply));
|
||||
evhttp_send_reply(request, http_status_code, "OK", buffer);
|
||||
evbuffer_free(buffer);
|
||||
}
|
||||
|
||||
static void stm_command_send_reply(struct evhttp_request *request, struct monitor_reply *reply)
|
||||
{
|
||||
struct evbuffer *buffer = evbuffer_new();
|
||||
sds reply_str = monitor_reply_to_string(reply);
|
||||
evbuffer_add(buffer, reply_str, sdslen(reply_str));
|
||||
evhttp_send_reply(request, reply->http_code, reply->http_reason, buffer);
|
||||
evbuffer_free(buffer);
|
||||
sdsfree(reply_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
static void stm_command_notfound(struct evhttp_request *request, UNUSED void *arg)
|
||||
{
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
const char *req_str_uri = evhttp_request_get_uri(request);
|
||||
struct evkeyvalq headers = {};
|
||||
evhttp_parse_query(req_str_uri, &headers);
|
||||
const char *raw_cmd_content = evhttp_find_header(&headers, STM_RESTFUL_URI_CMD_KEY);
|
||||
|
||||
stm_command_send_reply(request, monitor_reply_new_error(error_format_unknown_command, raw_cmd_content));
|
||||
STELLAR_LOG_ERROR(stm->logger_ref, STM_LOG_MODULE_NAME, "invlid http uri: %s\r\n", evhttp_request_get_uri(request));
|
||||
evhttp_clear_headers(&headers);
|
||||
}
|
||||
|
||||
static void stm_exec_command(struct stellar_monitor *stm, struct evhttp_request *request, const char *cmd_line)
|
||||
{
|
||||
stm_spinlock_lock(stm->lock);
|
||||
monitor_cmd_cb *cmd_cb = stm_cmd_assistant_get_cb(stm->aide, cmd_line);
|
||||
if (NULL == cmd_cb)
|
||||
{
|
||||
stm_command_notfound(request, NULL);
|
||||
stm_spinlock_unlock(stm->lock);
|
||||
return;
|
||||
}
|
||||
void *cmd_user_arg = stm_cmd_assistant_get_user_arg(stm->aide, cmd_line);
|
||||
int argc;
|
||||
sds *cmd_argv = sdssplitargs(cmd_line, &argc);
|
||||
struct monitor_reply *reply = cmd_cb(stm, argc, cmd_argv, cmd_user_arg);
|
||||
|
||||
stm_command_send_reply(request, reply);
|
||||
sdsfreesplitres(cmd_argv, argc);
|
||||
stm_spinlock_unlock(stm->lock);
|
||||
}
|
||||
|
||||
static void stm_new_request_cb(struct evhttp_request *request, UNUSED void *privParams)
|
||||
{
|
||||
__stm_libevevt_callback_thread_local_tid = pthread_self();
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
struct evhttp_connection *ev_conn = evhttp_request_get_connection(request);
|
||||
stm->current_conn.current_evconn_ref = ev_conn;
|
||||
stm_spinlock_lock(stm->lock);
|
||||
stm_connection_insert(ev_conn);
|
||||
stm_spinlock_unlock(stm->lock);
|
||||
evhttp_connection_set_closecb(ev_conn, on_connection_close_cb, request);
|
||||
// evhttp_request_set_error_cb(request, on_request_error_cb);
|
||||
|
||||
const char *req_str_uri = evhttp_request_get_uri(request);
|
||||
char *peer_ip_addr;
|
||||
uint16_t peer_port;
|
||||
evhttp_connection_get_peer(ev_conn, &peer_ip_addr, &peer_port);
|
||||
|
||||
STELLAR_LOG_INFO(stm->logger_ref, STM_LOG_MODULE_NAME, "new cli request, client:%s:%u, uri: %s\n", peer_ip_addr, peer_port, req_str_uri);
|
||||
|
||||
struct evkeyvalq headers = {};
|
||||
evhttp_parse_query(req_str_uri, &headers);
|
||||
const char *raw_cmd_content = evhttp_find_header(&headers, STM_RESTFUL_URI_CMD_KEY);
|
||||
if (NULL == raw_cmd_content)
|
||||
{
|
||||
stm_command_send_reply_by_cstr(request, HTTP_BADREQUEST, "http uri syntax error\r\n", NULL);
|
||||
evhttp_clear_headers(&headers);
|
||||
return;
|
||||
}
|
||||
stm_exec_command(stm, request, raw_cmd_content);
|
||||
evhttp_clear_headers(&headers);
|
||||
}
|
||||
|
||||
static int stm_event_http_init(struct stellar_monitor *stm)
|
||||
{
|
||||
// Create a new event handler
|
||||
stm->evt_base = event_base_new();
|
||||
// Create a http server using that handler
|
||||
stm->evt_http_server = evhttp_new(stm->evt_base);
|
||||
// Limit serving GET requests
|
||||
evhttp_set_allowed_methods(stm->evt_http_server, EVHTTP_REQ_GET);
|
||||
|
||||
char restful_path[256] = {0}; /* must start with '/' */
|
||||
snprintf(restful_path, sizeof(restful_path), "/%s/%s", STM_RESTFUL_VERSION, STM_RESTFUL_RESOURCE);
|
||||
evhttp_set_cb(stm->evt_http_server, restful_path, stm_new_request_cb, stm->evt_base);
|
||||
|
||||
// Set the callback for anything not recognized
|
||||
evhttp_set_gencb(stm->evt_http_server, stm_command_notfound, NULL);
|
||||
if (evhttp_bind_socket(stm->evt_http_server, stm->config->listen_ipaddr, stm->config->listen_port_host_order) != 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "Could not bind to %s:%u\r\n", stm->config->listen_ipaddr, stm->config->listen_port_host_order);
|
||||
return -1;
|
||||
}
|
||||
evhttp_set_timeout(stm->evt_http_server, stm->config->connection_idle_timeout);
|
||||
STELLAR_LOG_INFO(stm->logger_ref, STM_LOG_MODULE_NAME, "accept http uri path: %s\r\n", restful_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *stm_event_main_loop(void *arg)
|
||||
{
|
||||
struct stellar_monitor *stm = (struct stellar_monitor *)arg;
|
||||
stm_save_thread_local_context(stm);
|
||||
event_base_dispatch(stm->evt_base);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void stm_event_http_free(struct stellar_monitor *stm)
|
||||
{
|
||||
event_base_loopbreak(stm->evt_base);
|
||||
pthread_cancel(stm->evt_main_loop_tid);
|
||||
pthread_join(stm->evt_main_loop_tid, NULL);
|
||||
evhttp_free(stm->evt_http_server);
|
||||
// event_free(stm->ev_timeout);
|
||||
event_base_free(stm->evt_base);
|
||||
}
|
||||
|
||||
static void stm_server_set_default_cfg(struct stellar_monitor_config *config)
|
||||
{
|
||||
config->ringbuf_size = STM_RINGBUF_SIZE;
|
||||
config->connection_idle_timeout = STM_CONNECTION_IDLE_TIMEOUT;
|
||||
config->cli_request_timeout = STM_REQUEST_TIMEOUT;
|
||||
config->listen_ipaddr = "0.0.0.0";
|
||||
config->listen_port_host_order = STM_SERVER_LISTEN_PORT;
|
||||
config->data_link_bind_port_host_order = STM_TZSP_UDP_PORT;
|
||||
config->output_interval_ms = STM_STAT_OUTPUT_INTERVAL_MS;
|
||||
}
|
||||
|
||||
int stellar_monitor_set_gettime_callback(struct stellar_monitor *stm, int (*gettime_cb)(struct timeval *tv, struct timezone *tz))
|
||||
{
|
||||
if (NULL == gettime_cb)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
stm->gettime_cb = gettime_cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stellar_monitor_config *stellar_monitor_config_new(const char *toml_file)
|
||||
{
|
||||
struct stellar_monitor_config *config = CALLOC(struct stellar_monitor_config, 1);
|
||||
stm_server_set_default_cfg(config);
|
||||
int64_t int64_val = 0;
|
||||
char errbuf[256];
|
||||
FILE *fp = NULL;
|
||||
toml_table_t *root = NULL;
|
||||
toml_table_t *table = NULL;
|
||||
toml_raw_t ptr = NULL;
|
||||
|
||||
fp = fopen(toml_file, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
fprintf(stderr, "config file %s open failed, %s", toml_file, strerror(errno));
|
||||
goto fail_exit;
|
||||
}
|
||||
|
||||
root = toml_parse_file(fp, errbuf, sizeof(errbuf));
|
||||
if (root == NULL)
|
||||
{
|
||||
fprintf(stderr, "config file %s parse failed, %s", toml_file, errbuf);
|
||||
goto fail_exit;
|
||||
}
|
||||
|
||||
table = toml_table_in(root, "monitor");
|
||||
if (table == NULL)
|
||||
{
|
||||
fprintf(stderr, "config file %s missing [monitor]", toml_file);
|
||||
goto fail_exit;
|
||||
}
|
||||
|
||||
/* listen_port */
|
||||
ptr = toml_raw_in(table, "listen_port");
|
||||
if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
|
||||
{
|
||||
if (int64_val < 1 || int64_val > 65535)
|
||||
{
|
||||
fprintf(stderr, "invalid monitor.listen_port %ld\n", int64_val);
|
||||
FREE(config);
|
||||
goto fail_exit;
|
||||
}
|
||||
config->listen_port_host_order = (uint16_t)int64_val;
|
||||
}
|
||||
|
||||
/* data link bind port */
|
||||
ptr = toml_raw_in(table, "data_link_bind_port");
|
||||
if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
|
||||
{
|
||||
if (int64_val < 1 || int64_val > 65535)
|
||||
{
|
||||
fprintf(stderr, "invalid monitor.data_link_bind_port %ld\n", int64_val);
|
||||
FREE(config);
|
||||
goto fail_exit;
|
||||
}
|
||||
config->data_link_bind_port_host_order = (uint16_t)int64_val;
|
||||
}
|
||||
|
||||
/* connection_idle_timeout */
|
||||
ptr = toml_raw_in(table, "connection_idle_timeout");
|
||||
if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
|
||||
{
|
||||
if (int64_val < 1 || int64_val > 3600)
|
||||
{
|
||||
fprintf(stderr, "invalid monitor.connection_idle_timeout %ld, should be [1, 3600]\n", int64_val);
|
||||
FREE(config);
|
||||
goto fail_exit;
|
||||
}
|
||||
config->connection_idle_timeout = (int)int64_val;
|
||||
}
|
||||
|
||||
/* cli_request_timeout */
|
||||
ptr = toml_raw_in(table, "cli_request_timeout");
|
||||
if (ptr != NULL || toml_rtoi(ptr, &int64_val) == 0)
|
||||
{
|
||||
if (int64_val < 1 || int64_val > 360)
|
||||
{
|
||||
fprintf(stderr, "invalid monitor.cli_request_timeout %ld, , should be [1, 360]\n", int64_val);
|
||||
FREE(config);
|
||||
goto fail_exit;
|
||||
}
|
||||
config->cli_request_timeout = (int)int64_val;
|
||||
}
|
||||
|
||||
/* stat */
|
||||
ptr = toml_raw_in(table, "stat_output_path");
|
||||
if (ptr == NULL || toml_rtos(ptr, &config->output_path) != 0)
|
||||
{
|
||||
config->output_path = strdup(STM_STAT_OUTPUT_PATH);
|
||||
}
|
||||
|
||||
ptr = toml_raw_in(table, "stat_output_interval_ms");
|
||||
if (ptr != NULL && toml_rtoi(ptr, &int64_val) == 0)
|
||||
{
|
||||
if (int64_val < 1000 || int64_val > 1000 * 60)
|
||||
{
|
||||
fprintf(stderr, "invalid monitor.stat_output_interval_ms %ld, , should be [1, 600000]\n", int64_val);
|
||||
FREE(config);
|
||||
goto fail_exit;
|
||||
}
|
||||
config->output_interval_ms = (int)int64_val;
|
||||
}
|
||||
|
||||
fail_exit:
|
||||
if (root)
|
||||
{
|
||||
toml_free(root);
|
||||
}
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
struct stellar_monitor *stellar_monitor_get(void)
|
||||
{
|
||||
if (pthread_self() != __thread_local_tid)
|
||||
{
|
||||
assert(0);
|
||||
// fprintf(stderr, "ERR stellar_monitor_get() failed, caller must in same thread context!\n");
|
||||
return NULL;
|
||||
}
|
||||
return __thread_local_stm;
|
||||
}
|
||||
|
||||
// support dynamic register command, independent of the order of initialization
|
||||
int monitor_register_cmd(struct stellar_monitor *stm, const char *cmd, monitor_cmd_cb *cb, const char *flags,
|
||||
const char *hint, const char *desc, void *arg)
|
||||
{
|
||||
stm_spinlock_lock(stm->lock);
|
||||
int ret = stm_cmd_assistant_register_cmd(stm->aide, cmd, cb, arg, flags, hint, desc);
|
||||
stm_spinlock_unlock(stm->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int monitor_register_connection_close_cb(struct stellar_monitor *stm, monitor_connection_close_cb *cb, void *arg)
|
||||
{
|
||||
stm_spinlock_lock(stm->lock);
|
||||
struct stm_conn_close_cb_manager *ele = CALLOC(struct stm_conn_close_cb_manager, 1);
|
||||
ele->cb = cb;
|
||||
ele->arg = arg;
|
||||
DL_APPEND(stm->conn_close_mgr, ele);
|
||||
stm_spinlock_unlock(stm->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct monitor_reply *monitor_cmd_show_brief_cb(struct stellar_monitor *stm)
|
||||
{
|
||||
sds cmd_brief = sdsempty();
|
||||
cmd_brief = sdscatfmt(cmd_brief, "%s, %s\r\n", "\"command\"", "description");
|
||||
cmd_brief = sdscatfmt(cmd_brief, "-----------------------------\r\n");
|
||||
sds cmd_brief_cont = stm_cmd_assistant_list_cmd_brief(stm->aide);
|
||||
cmd_brief = sdscatsds(cmd_brief, cmd_brief_cont);
|
||||
struct monitor_reply *reply = monitor_reply_new_string("%s", cmd_brief);
|
||||
sdsfree(cmd_brief);
|
||||
sdsfree(cmd_brief_cont);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static struct monitor_reply *monitor_cmd_show_verbose_cb(struct stellar_monitor *stm)
|
||||
{
|
||||
sds cmd_verbose = stm_cmd_assistant_list_cmd_verbose(stm->aide);
|
||||
struct monitor_reply *reply = monitor_reply_new_string("%s", cmd_verbose);
|
||||
sdsfree(cmd_verbose);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static struct monitor_reply *monitor_server_builtin_show_command_cb(struct stellar_monitor *stm UNUSED, int argc, char *argv[], UNUSED void *arg)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
return monitor_reply_new_error(error_format_wrong_number_of_args, "show command");
|
||||
}
|
||||
if (stm_strncasecmp_exactly(argv[2], "brief", 5) == 0)
|
||||
{
|
||||
return monitor_cmd_show_brief_cb((struct stellar_monitor *)arg);
|
||||
}
|
||||
else if (stm_strncasecmp_exactly(argv[2], "verbose", 7) == 0)
|
||||
{
|
||||
return monitor_cmd_show_verbose_cb((struct stellar_monitor *)arg);
|
||||
}
|
||||
return monitor_reply_new_error(error_format_unknown_arg, argv[2]);
|
||||
}
|
||||
|
||||
static struct monitor_reply *monitor_server_builtin_ping_cb(struct stellar_monitor *stm UNUSED, int argc, char *argv[], UNUSED void *arg)
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
return monitor_reply_new_string("pong");
|
||||
}
|
||||
else if (argc == 2)
|
||||
{
|
||||
return monitor_reply_new_string("%s", argv[1]);
|
||||
}
|
||||
return monitor_reply_new_error(error_format_wrong_number_of_args, "ping");
|
||||
}
|
||||
|
||||
static struct monitor_reply *monitor_server_builtin_who_cb(struct stellar_monitor *stm, int argc UNUSED, char *argv[] UNUSED, UNUSED void *arg)
|
||||
{
|
||||
struct stm_connection_manager *conn_mgr = stm->connection_mgr;
|
||||
struct stm_connection_manager *ele, *tmp;
|
||||
sds who = sdsempty();
|
||||
char timestr[64];
|
||||
DL_FOREACH_SAFE(conn_mgr, ele, tmp)
|
||||
{
|
||||
struct timeval tv = ele->link_start_time;
|
||||
struct tm *tm = localtime(&tv.tv_sec);
|
||||
strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tm);
|
||||
who = sdscatprintf(who, "%s %s:%u", timestr, ele->peer_ipaddr, ele->peer_port_host_order);
|
||||
if (stm->current_conn.current_evconn_ref == ele->conn)
|
||||
{
|
||||
who = sdscat(who, "\033[1m [current]\033[0m");
|
||||
}
|
||||
who = sdscat(who, "\r\n");
|
||||
}
|
||||
sdsIncrLen(who, -2); // delete last \r\n
|
||||
struct monitor_reply *reply = monitor_reply_new_string("%s", who);
|
||||
sdsfree(who);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static int stm_builtin_cmd_register(struct stellar_monitor *stm)
|
||||
{
|
||||
int ret = 0;
|
||||
ret += monitor_register_cmd(stm, "show command", monitor_server_builtin_show_command_cb, "readonly", "[ brief|verbose ]", "show all registered commands info", (void *)stm);
|
||||
assert(ret == 0);
|
||||
ret += monitor_register_cmd(stm, "who", monitor_server_builtin_who_cb, "readonly", "<cr>", "show who is logged on", (void *)stm);
|
||||
assert(ret == 0);
|
||||
ret += monitor_register_cmd(stm, "ping", monitor_server_builtin_ping_cb, "readonly", "[message]", "ping the server", (void *)stm);
|
||||
assert(ret == 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct monitor_connection *monitor_get_current_connection(struct stellar_monitor *monitor)
|
||||
{
|
||||
if (__stm_libevevt_callback_thread_local_tid != pthread_self())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &monitor->current_conn;
|
||||
}
|
||||
|
||||
int monitor_get_peer_addr(struct monitor_connection *conn, char **peer_ip, uint16_t *peer_port)
|
||||
{
|
||||
if (NULL == conn || conn->current_evconn_ref == NULL)
|
||||
{
|
||||
if (peer_ip)
|
||||
{
|
||||
*peer_ip = NULL;
|
||||
}
|
||||
if (peer_port)
|
||||
{
|
||||
*peer_port = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
evhttp_connection_get_peer(conn->current_evconn_ref, peer_ip, peer_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void monitor_free(struct stellar_monitor *stm)
|
||||
{
|
||||
STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "free and exit\n");
|
||||
stm_event_http_free(stm);
|
||||
stm_stat_free(stm->stat);
|
||||
stm_cmd_assistant_free(stm->aide);
|
||||
stm_spinlock_free(stm->lock);
|
||||
if (stm->rpc_ins_array)
|
||||
{
|
||||
for (int tid = 0; tid < stm->worker_thread_num; tid++)
|
||||
{
|
||||
monitor_rpc_free(stm->rpc_ins_array[tid]);
|
||||
}
|
||||
free(stm->rpc_ins_array);
|
||||
}
|
||||
|
||||
__thread_local_stm = NULL;
|
||||
FREE(stm->config->output_path);
|
||||
FREE(stm->config);
|
||||
FREE(stm);
|
||||
}
|
||||
|
||||
struct stellar_monitor *monitor_module_to_monitor(struct module *monitor_module)
|
||||
{
|
||||
if (monitor_module == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return (struct stellar_monitor *)module_get_ctx(monitor_module);
|
||||
}
|
||||
|
||||
struct stellar_monitor *stellar_module_get_monitor(struct module_manager *mod_mgr)
|
||||
{
|
||||
assert(mod_mgr);
|
||||
struct module *monitor_mod = module_manager_get_module(mod_mgr, MONITOR_MODULE_NAME);
|
||||
return monitor_module_to_monitor(monitor_mod);
|
||||
}
|
||||
|
||||
void monitor_on_exit(struct module_manager *mod_mgr __attribute__((unused)), struct module *mod)
|
||||
{
|
||||
if (mod)
|
||||
{
|
||||
struct stellar_monitor *stm = module_get_ctx(mod);
|
||||
monitor_free(stm);
|
||||
module_free(mod);
|
||||
}
|
||||
}
|
||||
|
||||
struct stellar_monitor *monitor_new(const char *toml_file, struct module_manager *mod_mgr, struct logger *logh)
|
||||
{
|
||||
struct stellar_monitor *stm = (struct stellar_monitor *)calloc(1, sizeof(struct stellar_monitor));
|
||||
stm->logger_ref = logh;
|
||||
stm->mod_mgr_ref = mod_mgr;
|
||||
|
||||
struct stellar_monitor_config *config = stellar_monitor_config_new(toml_file);
|
||||
if (NULL == config)
|
||||
{
|
||||
STELLAR_LOG_FATAL(logh, STM_LOG_MODULE_NAME, "get config failed!\n");
|
||||
goto fail_exit;
|
||||
}
|
||||
stm->config = config;
|
||||
|
||||
stm->worker_thread_num = module_manager_get_max_thread_num(mod_mgr);
|
||||
|
||||
stm->lock = stm_spinlock_new();
|
||||
stm->worker_thread_num = module_manager_get_max_thread_num(mod_mgr);
|
||||
assert(stm->worker_thread_num > 0);
|
||||
stm->gettime_cb = gettimeofday;
|
||||
stm->aide = stm_cmd_assistant_new();
|
||||
if (stm_event_http_init(stm) < 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "libevent http server init() failed!\n");
|
||||
goto fail_exit;
|
||||
}
|
||||
stm->stat = stm_stat_init(stm);
|
||||
stm_builtin_cmd_register(stm);
|
||||
stm_save_thread_local_context(stm);
|
||||
pthread_create(&stm->evt_main_loop_tid, NULL, stm_event_main_loop, (void *)stm);
|
||||
sds config_print = stm_config_print(config);
|
||||
STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "config: %s\n", config_print);
|
||||
sdsfree(config_print);
|
||||
|
||||
stm->rpc_ins_array = (struct monitor_rpc **)calloc(stm->worker_thread_num, sizeof(struct monitor_rpc *));
|
||||
for (int tid = 0; tid < stm->worker_thread_num; tid++)
|
||||
{
|
||||
stm->rpc_ins_array[tid] = monitor_rpc_new(stm, mod_mgr);
|
||||
if (stm->rpc_ins_array[tid] == NULL)
|
||||
{
|
||||
STELLAR_LOG_FATAL(stm->logger_ref, STM_LOG_MODULE_NAME, "rpc init failed\n");
|
||||
goto fail_exit;
|
||||
}
|
||||
}
|
||||
|
||||
stm_save_thread_local_context(stm);
|
||||
return stm;
|
||||
|
||||
fail_exit:
|
||||
monitor_free(stm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct module *monitor_on_init(struct module_manager *mod_mgr)
|
||||
{
|
||||
assert(mod_mgr);
|
||||
const char *toml_file = module_manager_get_toml_path(mod_mgr);
|
||||
assert(toml_file);
|
||||
struct logger *logh = module_manager_get_logger(mod_mgr);
|
||||
assert(logh);
|
||||
|
||||
struct stellar_monitor *stm = monitor_new(toml_file, mod_mgr, logh);
|
||||
struct module *stm_mod = module_new(MONITOR_MODULE_NAME, (void *)stm);
|
||||
if (stm_mod == NULL)
|
||||
{
|
||||
STELLAR_LOG_FATAL(logh, STM_LOG_MODULE_NAME, "moudule new '%s' fail\n", MONITOR_MODULE_NAME);
|
||||
monitor_free(stm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// show_session_enforcer_init(mod_mgr, stm);
|
||||
// stm_pktdump_enforcer_init(mod_mgr, stm);
|
||||
|
||||
STELLAR_LOG_FATAL(logh, STM_LOG_MODULE_NAME, "init success\n");
|
||||
return stm_mod;
|
||||
}
|
||||
72
infra/monitor/monitor_spinlock.c
Normal file
72
infra/monitor/monitor_spinlock.c
Normal file
@@ -0,0 +1,72 @@
|
||||
// https://www.cs.utexas.edu/~pingali/CS378/2015sp/lectures/Spinlocks%20and%20Read-Write%20Locks.htm
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#if 0 /* use gcc builtin function */
|
||||
struct stm_spinlock
|
||||
{
|
||||
long value;
|
||||
};
|
||||
|
||||
struct stm_spinlock *stm_spinlock_new(void)
|
||||
{
|
||||
struct stm_spinlock *splock = (struct stm_spinlock *)calloc(1, sizeof(struct stm_spinlock));
|
||||
return splock;
|
||||
}
|
||||
|
||||
void stm_spinlock_lock(struct stm_spinlock *splock)
|
||||
{
|
||||
while (__sync_lock_test_and_set(&splock->value, 1))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void stm_spinlock_unlock(struct stm_spinlock *splock)
|
||||
{
|
||||
__sync_lock_release(&splock->value);
|
||||
}
|
||||
|
||||
void stm_spinlock_free(struct stm_spinlock *splock)
|
||||
{
|
||||
if (splock)
|
||||
{
|
||||
free(splock);
|
||||
}
|
||||
}
|
||||
#else /* pthread spin lock */
|
||||
struct stm_spinlock
|
||||
{
|
||||
pthread_spinlock_t lock_ins;
|
||||
};
|
||||
|
||||
struct stm_spinlock *stm_spinlock_new(void)
|
||||
{
|
||||
struct stm_spinlock *splock = (struct stm_spinlock *)calloc(1, sizeof(struct stm_spinlock));
|
||||
pthread_spin_init(&splock->lock_ins, PTHREAD_PROCESS_PRIVATE);
|
||||
return splock;
|
||||
}
|
||||
|
||||
void stm_spinlock_lock(struct stm_spinlock *splock)
|
||||
{
|
||||
pthread_spin_lock(&splock->lock_ins);
|
||||
}
|
||||
|
||||
void stm_spinlock_unlock(struct stm_spinlock *splock)
|
||||
{
|
||||
pthread_spin_unlock(&splock->lock_ins);
|
||||
}
|
||||
|
||||
void stm_spinlock_free(struct stm_spinlock *splock)
|
||||
{
|
||||
if (splock)
|
||||
{
|
||||
pthread_spin_destroy(&splock->lock_ins);
|
||||
free(splock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
8
infra/monitor/monitor_spinlock.h
Normal file
8
infra/monitor/monitor_spinlock.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
struct stm_spinlock;
|
||||
|
||||
struct stm_spinlock *stm_spinlock_new(void);
|
||||
void stm_spinlock_lock(struct stm_spinlock *splock);
|
||||
void stm_spinlock_unlock(struct stm_spinlock *splock);
|
||||
void stm_spinlock_free(struct stm_spinlock *splock);
|
||||
76
infra/monitor/monitor_stat.c
Normal file
76
infra/monitor/monitor_stat.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <getopt.h>
|
||||
#include <evhttp.h>
|
||||
#include "monitor_private.h"
|
||||
#include <fieldstat/fieldstat_easy.h>
|
||||
|
||||
static const char *stm_stat_field_name[] = {
|
||||
"connection_new",
|
||||
"connection_close",
|
||||
"request_succ",
|
||||
"request_err",
|
||||
"response_succ",
|
||||
"response_err",
|
||||
NULL,
|
||||
};
|
||||
|
||||
long long stm_get_stat_count(struct stm_stat *stat, enum stm_stat_type type)
|
||||
{
|
||||
if ((int)type < 0 || type >= STM_STAT_MAX)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return stat->counters[type].count;
|
||||
}
|
||||
|
||||
long long stm_get_stat_bytes(struct stm_stat *stat, enum stm_stat_type type)
|
||||
{
|
||||
if ((int)type < 0 || type >= STM_STAT_MAX)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return stat->counters[type].bytes;
|
||||
}
|
||||
|
||||
void stm_stat_update(struct stm_stat *stat, int thread_idx, enum stm_stat_type type, long long value)
|
||||
{
|
||||
if ((int)type < 0 || type >= STM_STAT_MAX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
fieldstat_easy_counter_incrby(stat->fs4_ins, thread_idx, stat->counters[type].counter_id, NULL, 0, value);
|
||||
}
|
||||
|
||||
struct stm_stat *stm_stat_init(struct stellar_monitor *stm)
|
||||
{
|
||||
const struct stellar_monitor_config *config = stm->config;
|
||||
assert(sizeof(stm_stat_field_name) / sizeof(stm_stat_field_name[0]) == STM_STAT_MAX + 1);
|
||||
struct stm_stat *stat = CALLOC(struct stm_stat, 1);
|
||||
/* worker thread count + 1, reserved for libevent callback thread context */
|
||||
stat->fs4_ins = fieldstat_easy_new(stm->worker_thread_num + 1, "monitor", NULL, 0);
|
||||
for (int i = 0; stm_stat_field_name[i] != NULL; i++)
|
||||
{
|
||||
stat->counters[i].counter_id = fieldstat_easy_register_counter(stat->fs4_ins, stm_stat_field_name[i]);
|
||||
}
|
||||
fieldstat_easy_enable_auto_output(stat->fs4_ins, config->output_path, MAX(config->output_interval_ms / 1000, 1));
|
||||
return stat;
|
||||
}
|
||||
|
||||
void stm_stat_free(struct stm_stat *stat)
|
||||
{
|
||||
if (NULL == stat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (stat->fs4_ins)
|
||||
{
|
||||
fieldstat_easy_free(stat->fs4_ins);
|
||||
}
|
||||
FREE(stat);
|
||||
}
|
||||
83
infra/monitor/monitor_transaction.c
Normal file
83
infra/monitor/monitor_transaction.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <pthread.h>
|
||||
#include "uthash/utlist.h"
|
||||
#include "monitor_private.h"
|
||||
|
||||
/******************************************** connection manager *****************************************/
|
||||
struct stm_connection_manager *stm_connection_insert(struct evhttp_connection *evconn)
|
||||
{
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
struct stm_connection_manager *conn_mgr = stm->connection_mgr;
|
||||
struct stm_connection_manager *ele, *tmp;
|
||||
DL_FOREACH_SAFE(conn_mgr, ele, tmp) // check if current connection already exist
|
||||
{
|
||||
if (ele->conn == evconn)
|
||||
{
|
||||
stm->gettime_cb(&ele->last_active_time, NULL);
|
||||
return ele;
|
||||
}
|
||||
}
|
||||
|
||||
stm_stat_update(stm->stat, stm->worker_thread_num, STM_STAT_CLI_CONNECTION_NEW, 1);
|
||||
struct stm_connection_manager *new_conn = (struct stm_connection_manager *)calloc(1, sizeof(struct stm_connection_manager));
|
||||
char *tmp_ip_addr;
|
||||
uint16_t tmp_port;
|
||||
evhttp_connection_get_peer(evconn, &tmp_ip_addr, &tmp_port);
|
||||
if (tmp_ip_addr)
|
||||
{
|
||||
strncpy(new_conn->peer_ipaddr, tmp_ip_addr, INET6_ADDRSTRLEN - 1);
|
||||
}
|
||||
new_conn->peer_port_host_order = tmp_port;
|
||||
stm->gettime_cb(&new_conn->link_start_time, NULL);
|
||||
new_conn->last_active_time = new_conn->link_start_time;
|
||||
new_conn->conn = evconn;
|
||||
DL_APPEND(stm->connection_mgr, new_conn);
|
||||
return new_conn;
|
||||
}
|
||||
|
||||
void stm_connection_delete(struct evhttp_connection *evconn)
|
||||
{
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
struct stm_connection_manager *conn_mgr = stm->connection_mgr;
|
||||
struct stm_connection_manager *ele, *tmp;
|
||||
DL_FOREACH_SAFE(conn_mgr, ele, tmp)
|
||||
{
|
||||
if (ele->conn == evconn)
|
||||
{
|
||||
DL_DELETE(conn_mgr, ele);
|
||||
FREE(ele);
|
||||
}
|
||||
}
|
||||
stm->connection_mgr = conn_mgr;
|
||||
}
|
||||
|
||||
void stm_connection_update(struct stm_connection_manager *conn_mgr, const struct evhttp_connection *evconn)
|
||||
{
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
struct stm_connection_manager *ele, *tmp;
|
||||
DL_FOREACH_SAFE(conn_mgr, ele, tmp)
|
||||
{
|
||||
if (ele->conn == evconn)
|
||||
{
|
||||
stm->gettime_cb(&ele->last_active_time, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const struct stm_connection_manager *stm_connection_search(const struct stm_connection_manager *conn_mgr_head, const struct evhttp_connection *evconn)
|
||||
{
|
||||
const struct stm_connection_manager *ele, *tmp;
|
||||
DL_FOREACH_SAFE(conn_mgr_head, ele, tmp)
|
||||
{
|
||||
if (ele->conn == evconn)
|
||||
{
|
||||
return ele;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
672
infra/monitor/monitor_utils.c
Normal file
672
infra/monitor/monitor_utils.c
Normal file
@@ -0,0 +1,672 @@
|
||||
#include "sds/sds.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <threads.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "monitor_private.h"
|
||||
#include "stellar/session.h"
|
||||
#include "tuple/tuple.h"
|
||||
|
||||
struct stm_key_value *stm_cmd_key_value_new(void)
|
||||
{
|
||||
struct stm_key_value *kv = (struct stm_key_value *)calloc(1, sizeof(struct stm_key_value));
|
||||
kv->tuple_num = 0;
|
||||
kv->tuple = NULL;
|
||||
return kv;
|
||||
}
|
||||
|
||||
void stm_cmd_key_value_append(struct stm_key_value **kv, const char *key, const char *value)
|
||||
{
|
||||
if (NULL == *kv)
|
||||
{
|
||||
*kv = stm_cmd_key_value_new();
|
||||
}
|
||||
struct stm_key_value *new_kv = *kv;
|
||||
new_kv->tuple = (struct stm_key_value_tuple *)realloc(new_kv->tuple, (new_kv->tuple_num + 1) * sizeof(struct stm_key_value_tuple));
|
||||
new_kv->tuple[new_kv->tuple_num].key = strdup(key);
|
||||
new_kv->tuple[new_kv->tuple_num].value = strdup(value);
|
||||
new_kv->tuple_num++;
|
||||
*kv = new_kv;
|
||||
}
|
||||
|
||||
void stm_cmd_key_value_free(struct stm_key_value *kv)
|
||||
{
|
||||
if (NULL == kv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < kv->tuple_num; i++)
|
||||
{
|
||||
FREE(kv->tuple[i].key);
|
||||
FREE(kv->tuple[i].value);
|
||||
}
|
||||
FREE(kv->tuple);
|
||||
FREE(kv);
|
||||
}
|
||||
|
||||
void stm_cmd_request_free(struct stm_cmd_request *req)
|
||||
{
|
||||
FREE(req);
|
||||
}
|
||||
|
||||
int stm_strncasecmp_exactly(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
if (NULL == s1 || NULL == s2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
size_t len1 = strlen(s1);
|
||||
size_t len2 = strlen(s2);
|
||||
|
||||
if (len1 != len2 || len1 != n)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return strncasecmp(s1, s2, n);
|
||||
}
|
||||
|
||||
const char *stm_session_state_ntop(enum session_state state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SESSION_STATE_OPENING:
|
||||
return "opening";
|
||||
break;
|
||||
case SESSION_STATE_ACTIVE:
|
||||
return "active";
|
||||
break;
|
||||
|
||||
case SESSION_STATE_CLOSING:
|
||||
return "closing";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *stm_session_flow_dir_ntop(uint32_t dir)
|
||||
{
|
||||
if (SESSION_SEEN_C2S_FLOW == dir)
|
||||
{
|
||||
return "C2S";
|
||||
}
|
||||
else if (SESSION_SEEN_S2C_FLOW == dir)
|
||||
{
|
||||
return "S2C";
|
||||
}
|
||||
else if ((SESSION_SEEN_C2S_FLOW | SESSION_SEEN_S2C_FLOW) == dir)
|
||||
{
|
||||
return "BIDIRECTION";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
int stm_time_range_pton(const char *time_str, time_t now, time_t time_range[2])
|
||||
{
|
||||
const char *delim = "-";
|
||||
char *save_ptr;
|
||||
const char *time_str_min_example = "last-1-days";
|
||||
if (NULL == time_str || NULL == time_range)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (strlen(time_str) < strlen(time_str_min_example))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
char *local_time_str = strdup(time_str);
|
||||
|
||||
char *fix_prefix = strtok_r(local_time_str, delim, &save_ptr);
|
||||
if (stm_strncasecmp_exactly(fix_prefix, "last", 4) != 0)
|
||||
{
|
||||
free(local_time_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *number_string = strtok_r(NULL, delim, &save_ptr);
|
||||
if (NULL == number_string)
|
||||
{
|
||||
free(local_time_str);
|
||||
return -1;
|
||||
}
|
||||
long number = strtol(number_string, NULL, 10);
|
||||
if (number <= 0 || number > 3600)
|
||||
{
|
||||
free(local_time_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *unit_string = strtok_r(NULL, delim, &save_ptr);
|
||||
if (NULL == unit_string)
|
||||
{
|
||||
free(local_time_str);
|
||||
return -1;
|
||||
}
|
||||
long long multiple_second = 1;
|
||||
if (stm_strncasecmp_exactly(unit_string, "seconds", 7) == 0)
|
||||
{
|
||||
multiple_second = 1;
|
||||
}
|
||||
else if (stm_strncasecmp_exactly(unit_string, "minutes", 7) == 0)
|
||||
{
|
||||
multiple_second = 60;
|
||||
}
|
||||
else if (stm_strncasecmp_exactly(unit_string, "hours", 5) == 0)
|
||||
{
|
||||
multiple_second = 60 * 60;
|
||||
}
|
||||
else if (stm_strncasecmp_exactly(unit_string, "days", 4) == 0)
|
||||
{
|
||||
multiple_second = 60 * 60 * 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(local_time_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((long long)now < number * multiple_second)
|
||||
{
|
||||
time_range[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
time_range[0] = now - number * multiple_second;
|
||||
}
|
||||
time_range[1] = now;
|
||||
while (strtok_r(NULL, delim, &save_ptr))
|
||||
{
|
||||
}
|
||||
free(local_time_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stm_time_in_range(time_t t, const time_t time_range[2])
|
||||
{
|
||||
if (NULL == time_range)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (t >= time_range[0] && t <= time_range[1])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t stm_inet_pton(const char *ipstr, void *ipv4_value, void *ipv6_value)
|
||||
{
|
||||
unsigned int tmp_ipv4;
|
||||
struct in6_addr tmp_ipv6;
|
||||
if (NULL == ipstr || NULL == ipv4_value || NULL == ipv6_value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (inet_pton(AF_INET, ipstr, &tmp_ipv4) == 1)
|
||||
{
|
||||
memcpy(ipv4_value, &tmp_ipv4, sizeof(int));
|
||||
return AF_INET;
|
||||
}
|
||||
if (inet_pton(AF_INET6, ipstr, &tmp_ipv6) == 1)
|
||||
{
|
||||
memcpy(ipv6_value, &tmp_ipv6, sizeof(struct in6_addr));
|
||||
return AF_INET6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a 128-bit mask given a network prefix.
|
||||
*/
|
||||
void in6_addr_mask(struct in6_addr *mask, uint8_t bits)
|
||||
{
|
||||
for (uint8_t i = 0; i < sizeof(mask->s6_addr); i++)
|
||||
{
|
||||
mask->s6_addr[i] = bits ? (uint8_t)0xFF << (8 - (bits > 8 ? 8 : bits)) : 0;
|
||||
|
||||
if (bits < 8)
|
||||
bits = 0;
|
||||
else
|
||||
bits -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the first address in a network given a mask.
|
||||
*/
|
||||
void in6_addr_network(struct in6_addr *network, const struct in6_addr *addr, const struct in6_addr *mask)
|
||||
{
|
||||
for (uint8_t i = 0; i < sizeof(network->s6_addr); i++)
|
||||
{
|
||||
network->s6_addr[i] = addr->s6_addr[i] & mask->s6_addr[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the last address in a network given a mask.
|
||||
*/
|
||||
void in6_addr_end(struct in6_addr *end, const struct in6_addr *addr, const struct in6_addr *mask)
|
||||
{
|
||||
for (uint8_t i = 0; i < sizeof(end->s6_addr); i++)
|
||||
{
|
||||
end->s6_addr[i] = (addr->s6_addr[i] & mask->s6_addr[i]) | ~mask->s6_addr[i];
|
||||
}
|
||||
}
|
||||
|
||||
int stm_ipv4_cidr_to_range(uint32_t ipaddr, uint32_t ipmask, uint32_t iprange[2])
|
||||
{
|
||||
uint32_t network_addr = ipaddr & ipmask;
|
||||
uint32_t broadcast_addr = (ipaddr & ipmask) | ~ipmask;
|
||||
iprange[0] = network_addr;
|
||||
iprange[1] = broadcast_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stm_ipv6_cidr_to_range(const struct in6_addr *ipaddr, const struct in6_addr *ipmask, struct in6_addr iprange[2])
|
||||
{
|
||||
struct in6_addr network_addr = {};
|
||||
struct in6_addr broadcast_addr = {};
|
||||
in6_addr_network(&network_addr, ipaddr, ipmask);
|
||||
in6_addr_end(&broadcast_addr, ipaddr, ipmask);
|
||||
memcpy(&iprange[0], &network_addr, sizeof(struct in6_addr));
|
||||
memcpy(&iprange[1], &broadcast_addr, sizeof(struct in6_addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t stm_ip_cidr_pton(const char *ipcidr, void *ipv4_value, void *ipv4_mask, void *ipv6_value, void *ipv6_mask)
|
||||
{
|
||||
int ipver = 0;
|
||||
const char *delim = "/";
|
||||
char *save_ptr;
|
||||
|
||||
if (NULL == ipcidr || NULL == ipv4_value || NULL == ipv4_mask || NULL == ipv6_value || NULL == ipv6_mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strchr(ipcidr, '/') == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
char *local_ip_cidr = strdup(ipcidr);
|
||||
|
||||
char *ipaddr = strtok_r(local_ip_cidr, delim, &save_ptr);
|
||||
ipver = stm_inet_pton(ipaddr, ipv4_value, ipv6_value);
|
||||
if (ipver != AF_INET && ipver != AF_INET6)
|
||||
{
|
||||
free(local_ip_cidr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *cidr = strtok_r(NULL, delim, &save_ptr);
|
||||
if (NULL == cidr)
|
||||
{
|
||||
free(local_ip_cidr);
|
||||
return 0;
|
||||
}
|
||||
int cidr_num = atoi(cidr);
|
||||
if (ipver == AF_INET)
|
||||
{
|
||||
if (cidr_num <= 0 || cidr_num > 32)
|
||||
{
|
||||
free(local_ip_cidr);
|
||||
return 0;
|
||||
}
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < cidr_num; i++)
|
||||
{
|
||||
mask |= (uint32_t)1 << (31 - i);
|
||||
}
|
||||
mask = ntohl(mask);
|
||||
memcpy(ipv4_mask, &mask, sizeof(int));
|
||||
}
|
||||
else if (ipver == AF_INET6)
|
||||
{
|
||||
if (cidr_num <= 0 || cidr_num > 128)
|
||||
{
|
||||
free(local_ip_cidr);
|
||||
return -1;
|
||||
}
|
||||
struct in6_addr mask = {};
|
||||
for (int i = 0; i < cidr_num; i++)
|
||||
{
|
||||
mask.s6_addr[i / 8] |= 1 << (7 - i % 8);
|
||||
}
|
||||
memcpy(ipv6_mask, &mask, sizeof(struct in6_addr));
|
||||
}
|
||||
|
||||
while (strtok_r(NULL, delim, &save_ptr))
|
||||
;
|
||||
|
||||
free(local_ip_cidr);
|
||||
return ipver;
|
||||
}
|
||||
|
||||
void stm_mem_fill_rand(char *buf, size_t len, unsigned char range_min, unsigned char range_max)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)buf;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
p[i] = (rand() % range_min + 1) + (range_max - range_min);
|
||||
}
|
||||
}
|
||||
|
||||
int stm_string_isdigit(const char *str)
|
||||
{
|
||||
if (NULL == str || strlen(str) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i < strlen(str); i++)
|
||||
{
|
||||
if (!isdigit(str[i]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* input("hello, world!", "hello", "hi", &output) -> output = "hi, world!"
|
||||
* return: the number of sub string have been replaced.
|
||||
*/
|
||||
int stm_replace_str(const char *raw, const char *search, const char *replace, char **outline)
|
||||
{
|
||||
const char *p = NULL;
|
||||
int search_len = strlen(search);
|
||||
int replace_len = strlen(replace);
|
||||
int raw_len = strlen(raw);
|
||||
|
||||
p = strstr(raw, search);
|
||||
if (p == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const char *remain_ptr = p + search_len;
|
||||
char *new_line = (char *)calloc(1, replace_len + (raw_len - search_len) + 1);
|
||||
strcpy(new_line, replace);
|
||||
strcpy(new_line + replace_len, remain_ptr);
|
||||
|
||||
*outline = new_line;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int stm_timeout(struct timeval start, struct timeval end, int timeout_sec)
|
||||
{
|
||||
return (end.tv_sec * 1000 * 1000 + end.tv_usec) - (start.tv_sec * 1000 * 1000 + start.tv_usec) >= timeout_sec * 1000 * 1000;
|
||||
}
|
||||
|
||||
struct monitor_reply *monitor_reply_nil(void)
|
||||
{
|
||||
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
|
||||
reply->type = MONITOR_REPLY_NIL;
|
||||
reply->http_code = HTTP_OK;
|
||||
reply->http_reason = "OK";
|
||||
return reply;
|
||||
}
|
||||
|
||||
/* string should without "\r\n", will add "\r\n" in monitor_reply_to_string() */
|
||||
struct monitor_reply *monitor_reply_new_string(const char *format, ...)
|
||||
{
|
||||
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
|
||||
reply->type = MONITOR_REPLY_STRING;
|
||||
reply->http_code = HTTP_OK;
|
||||
reply->http_reason = "OK";
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
reply->str = sdscatvprintf(sdsempty(), format, ap);
|
||||
reply->len = strlen(reply->str);
|
||||
va_end(ap);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/* string should without "\r\n", will add "\r\n" in monitor_reply_to_string() */
|
||||
struct monitor_reply *monitor_reply_new_error(const char *format, ...)
|
||||
{
|
||||
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
|
||||
reply->type = MONITOR_REPLY_ERROR;
|
||||
reply->http_code = HTTP_BADREQUEST;
|
||||
reply->http_reason = "ERROR";
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
reply->str = sdscatvprintf(sdsempty(), format, ap);
|
||||
reply->len = strlen(reply->str);
|
||||
va_end(ap);
|
||||
return reply;
|
||||
}
|
||||
|
||||
struct monitor_reply *monitor_reply_new_status(const char *format, ...)
|
||||
{
|
||||
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
|
||||
reply->type = MONITOR_REPLY_STATUS;
|
||||
reply->http_code = HTTP_OK;
|
||||
reply->http_reason = "OK";
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
reply->str = sdscatvprintf(sdsempty(), format, ap);
|
||||
reply->len = strlen(reply->str);
|
||||
va_end(ap);
|
||||
return reply;
|
||||
}
|
||||
|
||||
struct monitor_reply *monitor_reply_new_integer(long long integer)
|
||||
{
|
||||
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
|
||||
reply->type = MONITOR_REPLY_INTEGER;
|
||||
reply->http_code = HTTP_OK;
|
||||
reply->http_reason = "OK";
|
||||
reply->integer = integer;
|
||||
return reply;
|
||||
}
|
||||
|
||||
struct monitor_reply *monitor_reply_new_double(double dval)
|
||||
{
|
||||
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
|
||||
reply->type = MONITOR_REPLY_DOUBLE;
|
||||
reply->http_code = HTTP_OK;
|
||||
reply->http_reason = "OK";
|
||||
reply->dval = dval;
|
||||
return reply;
|
||||
}
|
||||
|
||||
sds monitor_reply_to_string(const struct monitor_reply *reply)
|
||||
{
|
||||
sds res = sdsempty();
|
||||
switch (reply->type)
|
||||
{
|
||||
case MONITOR_REPLY_INTEGER:
|
||||
res = sdscatprintf(res, "(integer) %lld\r\n", reply->integer);
|
||||
break;
|
||||
case MONITOR_REPLY_DOUBLE:
|
||||
res = sdscatprintf(res, "(double) %f\r\n", reply->dval);
|
||||
break;
|
||||
case MONITOR_REPLY_STRING:
|
||||
case MONITOR_REPLY_STATUS:
|
||||
res = sdscatlen(res, reply->str, reply->len);
|
||||
res = sdscat(res, "\r\n");
|
||||
break;
|
||||
case MONITOR_REPLY_NIL:
|
||||
res = sdscat(res, "(nil)\r\n");
|
||||
break;
|
||||
case MONITOR_REPLY_ERROR:
|
||||
res = sdscatprintf(res, "(error) %s\r\n", reply->str);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void monitor_reply_free(struct monitor_reply *reply)
|
||||
{
|
||||
switch (reply->type)
|
||||
{
|
||||
case MONITOR_REPLY_STRING:
|
||||
case MONITOR_REPLY_ERROR:
|
||||
case MONITOR_REPLY_STATUS:
|
||||
sdsfree(reply->str);
|
||||
reply->str = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(reply);
|
||||
}
|
||||
|
||||
static struct monitor_cli_args *monitor_util_search_cmd_args(const char *opt_name, const struct monitor_cli_args cli_args[], size_t cli_args_array_size)
|
||||
{
|
||||
if (NULL == cli_args)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (size_t i = 0; i < cli_args_array_size; i++)
|
||||
{
|
||||
if (stm_strncasecmp_exactly(opt_name, cli_args[i].short_opt, strlen(cli_args[i].short_opt)) == 0 || stm_strncasecmp_exactly(opt_name, cli_args[i].long_opt, strlen(cli_args[i].long_opt)) == 0)
|
||||
{
|
||||
return (struct monitor_cli_args *)&cli_args[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sds monitor_util_copy_arg_value(int argc, const char *argv[], int *copy_args_num)
|
||||
{
|
||||
sds res = sdsempty();
|
||||
int num = 0;
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
if ((strncmp(argv[i], "-", 1) == 0) || (strncmp(argv[i], "--", 2) == 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = sdscat(res, argv[i]);
|
||||
if ((i + 1 < argc) && (strncmp(argv[i + 1], "-", 1) != 0) && (strncmp(argv[i + 1], "--", 2) != 0)) // not the last one
|
||||
{
|
||||
res = sdscat(res, " ");
|
||||
}
|
||||
num++;
|
||||
}
|
||||
}
|
||||
*copy_args_num = num;
|
||||
return res;
|
||||
}
|
||||
|
||||
int monitor_util_parse_cmd_args(int argc, const char *argv[], struct monitor_cli_args cli_args[], size_t cli_args_array_size)
|
||||
{
|
||||
if (argc <= 0 || NULL == argv || NULL == cli_args)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int parse_stage = 0; // 0: arg; 1: value
|
||||
struct monitor_cli_args *one_arg = NULL;
|
||||
for (int i = 1; i < argc;) // skip program self name
|
||||
{
|
||||
if (0 == parse_stage)
|
||||
{
|
||||
one_arg = monitor_util_search_cmd_args(argv[i], cli_args, cli_args_array_size);
|
||||
if (NULL == one_arg)
|
||||
{
|
||||
fprintf(stderr, "unknown option: %s\n", argv[i]);
|
||||
return -1;
|
||||
}
|
||||
if (one_arg->require_arg_value && (i + 1 >= argc))
|
||||
{
|
||||
fprintf(stderr, "option requires value: %s\n", argv[i]);
|
||||
return -1;
|
||||
}
|
||||
parse_stage = 1;
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NULL == one_arg)
|
||||
{
|
||||
fprintf(stderr, "unknown option: %s\n", argv[i]);
|
||||
return -1;
|
||||
}
|
||||
int copy_args_num = 0;
|
||||
if (one_arg->value_is_multi_words)
|
||||
{
|
||||
one_arg->value = monitor_util_copy_arg_value(argc - i, &argv[i], ©_args_num);
|
||||
}
|
||||
else
|
||||
{
|
||||
one_arg->value = sdsnew(argv[i]);
|
||||
copy_args_num = 1;
|
||||
}
|
||||
i += copy_args_num;
|
||||
parse_stage = 0;
|
||||
one_arg = NULL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sds stm_config_print(const struct stellar_monitor_config *config)
|
||||
{
|
||||
sds res = sdsempty();
|
||||
res = sdscatprintf(res, "cli_request_timeout: %ds, ", config->cli_request_timeout);
|
||||
res = sdscatprintf(res, "connection_idle_timeout: %ds, ", config->connection_idle_timeout);
|
||||
res = sdscatprintf(res, "listen_ip_addr: %s, ", config->listen_ipaddr);
|
||||
res = sdscatprintf(res, "listen_port %u, ", config->listen_port_host_order);
|
||||
res = sdscatprintf(res, "data_link_bind_port: %u, ", config->data_link_bind_port_host_order);
|
||||
res = sdscatprintf(res, "stat_output_path: %s, ", config->output_path);
|
||||
res = sdscatprintf(res, "stat_output_interval_ms: %dms ", config->output_interval_ms);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *stm_http_url_encode(const char *originalText)
|
||||
{
|
||||
// allocate memory for the worst possible case (all characters need to be encoded)
|
||||
char *encodedText = (char *)malloc(sizeof(char) * strlen(originalText) * 3 + 1);
|
||||
const char *hex = "0123456789abcdef";
|
||||
int pos = 0;
|
||||
for (size_t i = 0; i < strlen(originalText); i++)
|
||||
{
|
||||
if (('a' <= originalText[i] && originalText[i] <= 'z') || ('A' <= originalText[i] && originalText[i] <= 'Z') || ('0' <= originalText[i] && originalText[i] <= '9'))
|
||||
{
|
||||
encodedText[pos++] = originalText[i];
|
||||
}
|
||||
else if (originalText[i] == ' ')
|
||||
{
|
||||
encodedText[pos++] = '%';
|
||||
encodedText[pos++] = hex[originalText[i] >> 4];
|
||||
encodedText[pos++] = hex[originalText[i] & 15];
|
||||
}
|
||||
/* todo: other characters ? */
|
||||
else
|
||||
{
|
||||
encodedText[pos++] = originalText[i];
|
||||
}
|
||||
}
|
||||
encodedText[pos] = '\0';
|
||||
return encodedText;
|
||||
}
|
||||
|
||||
const char *stm_get0_readable_session_addr(const struct tuple6 *addr, char *addr_buf, size_t max_buf_len)
|
||||
{
|
||||
tuple4_to_str((struct tuple4 *)addr, addr_buf, max_buf_len);
|
||||
return addr_buf;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
30
infra/monitor/monitor_utils.h
Normal file
30
infra/monitor/monitor_utils.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include <netinet/in.h>
|
||||
#include "stellar/session.h"
|
||||
#include "tuple/tuple.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
int stm_replace_str(const char *raw, const char *search, const char *replace, char **outline);
|
||||
int stm_strncasecmp_exactly(const char *s1, const char *s2, size_t n);
|
||||
struct stm_cmd_reply *stm_build_reply_error(const char *format, ...);
|
||||
struct stm_cmd_reply *stm_cmd_reply_dup(const struct stm_cmd_reply *src);
|
||||
const char *stm_session_flow_dir_ntop(uint32_t dir);
|
||||
int stm_time_range_pton(const char *time_str, time_t now, time_t time_range[2]);
|
||||
int stm_time_in_range(time_t t, const time_t time_range[2]);
|
||||
uint32_t stm_inet_pton(const char *ipstr, void *ipv4_value, void *ipv6_value);
|
||||
uint32_t stm_ip_cidr_pton(const char *ipcidr, void *ipv4_value, void *ipv4_mask, void *ipv6_value, void *ipv6_mask);
|
||||
int stm_ipv4_cidr_to_range(uint32_t ipaddr, uint32_t ipmask, uint32_t iprange[2]);
|
||||
int stm_ipv6_cidr_to_range(const struct in6_addr *ipaddr, const struct in6_addr *ipmask, struct in6_addr iprange[2]);
|
||||
void stm_mem_fill_rand(char *buf, size_t len, unsigned char range_min, unsigned char range_max);
|
||||
int stm_string_isdigit(const char *str);
|
||||
int stm_timeout(struct timeval start, struct timeval end, int timeout_sec);
|
||||
const char *stm_session_state_ntop(enum session_state state);
|
||||
const char *stm_get0_readable_session_addr(const struct tuple6 *addr, char *addr_buf, size_t max_buf_len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
10
infra/monitor/version.map
Normal file
10
infra/monitor/version.map
Normal file
@@ -0,0 +1,10 @@
|
||||
VERS_3.0{
|
||||
global:
|
||||
extern "C" {
|
||||
stellar_monitor_*;
|
||||
monitor_*;
|
||||
stm_*;
|
||||
stellar_module_get_monitor;
|
||||
};
|
||||
local: *;
|
||||
};
|
||||
@@ -8,7 +8,7 @@ extern "C"
|
||||
#include <stdbool.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "tuple.h"
|
||||
#include "tuple/tuple.h"
|
||||
#include "stellar/packet.h"
|
||||
|
||||
#define PACKET_MAX_LAYERS 32
|
||||
|
||||
@@ -593,5 +593,15 @@ void session_manager_on_thread_exit(struct module_manager *mod_mgr, int thread_i
|
||||
{
|
||||
struct session_manager *sess_mgr = module_get_ctx(mod);
|
||||
assert(sess_mgr);
|
||||
assert(thread_id < (int)sess_mgr->cfg->thread_num);
|
||||
|
||||
session_manager_clean(sess_mgr, thread_id);
|
||||
}
|
||||
|
||||
// temp add for show session command
|
||||
struct session_manager_rte *session_manager_get_runtime(struct session_manager *sess_mgr, uint16_t thread_id)
|
||||
{
|
||||
assert(sess_mgr);
|
||||
assert(thread_id < sess_mgr->cfg->thread_num);
|
||||
return sess_mgr->rte[thread_id];
|
||||
}
|
||||
@@ -7,6 +7,7 @@ extern "C"
|
||||
|
||||
#include "tuple.h"
|
||||
#include "stellar/session.h"
|
||||
#include "session_manager_cfg.h"
|
||||
|
||||
enum session_scan_flags
|
||||
{
|
||||
|
||||
@@ -74,5 +74,7 @@ global:
|
||||
lpi_plus_exit;
|
||||
lpi_plus_appid_subscribe;
|
||||
|
||||
monitor_on_init;
|
||||
monitor_on_exit;
|
||||
local: *;
|
||||
};
|
||||
|
||||
@@ -5,4 +5,5 @@ add_subdirectory(lpi_plus)
|
||||
#add_subdirectory(decoders/http)
|
||||
#add_subdirectory(decoders/socks)
|
||||
#add_subdirectory(decoders/stratum)
|
||||
#add_subdirectory(decoders/session_flags)
|
||||
#add_subdirectory(decoders/session_flags)
|
||||
add_subdirectory(monitor)
|
||||
507
test/decoders/http/http_gtest_llhttp.cpp
Normal file
507
test/decoders/http/http_gtest_llhttp.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "stellar/utils.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include <llhttp.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
struct gtest_http_counter
|
||||
{
|
||||
uint8_t flag_on_message_begin;
|
||||
uint8_t flag_on_url;
|
||||
uint8_t flag_on_method;
|
||||
uint8_t flag_on_status;
|
||||
uint8_t flag_on_version;
|
||||
uint8_t flag_on_header_field;
|
||||
uint8_t flag_on_header_field_complete;
|
||||
uint8_t flag_on_header_value;
|
||||
uint8_t flag_on_header_value_complete;
|
||||
uint8_t flag_on_headers_complete;
|
||||
uint8_t flag_on_body;
|
||||
uint8_t flag_on_message_complete;
|
||||
};
|
||||
|
||||
struct gtest_http_parm
|
||||
{
|
||||
llhttp_t llhttp_parser;
|
||||
llhttp_settings_t settings;
|
||||
struct gtest_http_counter count;
|
||||
};
|
||||
|
||||
static int on_message_begin(llhttp_t *parser)
|
||||
{
|
||||
printf("Message begin cb\n");
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_message_begin++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_url(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("URI cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_url++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_method(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("method cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_method++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_status(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("status cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_status++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_version(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("version cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_version++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_header_field(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("Header field cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_header_field++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_header_field_complete(llhttp_t *parser)
|
||||
{
|
||||
printf("Header field complete cb\n");
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_header_field_complete++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_header_value(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("Header value cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_header_value++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_header_value_complete(llhttp_t *parser)
|
||||
{
|
||||
printf("Header value complete cb\n");
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_header_value_complete++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_headers_complete(llhttp_t *parser)
|
||||
{
|
||||
printf("All Headers complete cb\n");
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_headers_complete++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_message_complete(llhttp_t *parser)
|
||||
{
|
||||
printf("Message complete cb\n");
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_message_complete++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_body(llhttp_t *parser, const char *at, size_t length)
|
||||
{
|
||||
printf("on_body cb: %.*s\n", (int)length, at);
|
||||
struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser);
|
||||
para->count.flag_on_body++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************** request test case *********************************/
|
||||
|
||||
static int gtest_llhttp_init(llhttp_t *parser, llhttp_type_t type, llhttp_settings_t *settings)
|
||||
{
|
||||
llhttp_settings_init(settings);
|
||||
settings->on_message_begin = on_message_begin;
|
||||
settings->on_url = on_url;
|
||||
settings->on_version = on_version;
|
||||
settings->on_status = on_status;
|
||||
settings->on_method = on_method;
|
||||
settings->on_header_field = on_header_field;
|
||||
settings->on_header_field_complete = on_header_field_complete;
|
||||
settings->on_header_value = on_header_value;
|
||||
settings->on_header_value_complete = on_header_value_complete;
|
||||
settings->on_headers_complete = on_headers_complete;
|
||||
settings->on_message_complete = on_message_complete;
|
||||
settings->on_body = on_body;
|
||||
|
||||
llhttp_init(parser, type, settings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, request_base)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, request_dir_error)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_TRUE(lerr != HPE_OK);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, request_uncompleted)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length:";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 0);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, request_hdr_pipeline)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example1.com\r\nContent-Length: 0\r\n\r\nGET /path/index.html HTTP/1.1\r\nHost: example2.com\r\nContent-Length: 0\r\n\r\nGET /path/index.html HTTP/1.1\r\nHost: example3.com\r\nContent-Length: 0\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 3);
|
||||
ASSERT_EQ(para.count.flag_on_url, 3);
|
||||
ASSERT_EQ(para.count.flag_on_method, 3);
|
||||
ASSERT_EQ(para.count.flag_on_version, 3);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 3);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 3);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, request_hdr_body_pipeline)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example1.com\r\nContent-Length: 1\r\n\r\nxGET /path/index.html HTTP/1.1\r\nHost: example2.com\r\nContent-Length: 2\r\n\r\nxxGET /path/index.html HTTP/1.1\r\nHost: example3.com\r\nContent-Length: 3\r\n\r\nxxx";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 3);
|
||||
ASSERT_EQ(para.count.flag_on_url, 3);
|
||||
ASSERT_EQ(para.count.flag_on_method, 3);
|
||||
ASSERT_EQ(para.count.flag_on_version, 3);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 3);
|
||||
ASSERT_EQ(para.count.flag_on_body, 3);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 3);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, request_body_chunked)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 2); // 2 chunks
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, v11_GET_no_content_length_hdr_with_body)
|
||||
{
|
||||
/*
|
||||
* v1.1
|
||||
* if no obvious content-length header, the body will be parsed as new header,
|
||||
* and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\n<html>some content<html/>";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_INVALID_METHOD);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 2);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, v10_GET_no_content_length_hdr_with_body)
|
||||
{
|
||||
/* v1.0
|
||||
* if no obvious content-length header, the body will be not parsed!
|
||||
* then raise a HPE_CLOSED_CONNECTION error!
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "GET /path/index.html HTTP/1.0\r\nHost: example.com\r\n\r\n\r\n<html>some content<html/>";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_CLOSED_CONNECTION);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, v11_POST_no_content_length_hdr_with_body)
|
||||
{
|
||||
/*
|
||||
* v1.1
|
||||
* if no obvious content-length header, the body will be parsed as new header,
|
||||
* and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\n<html>some content<html/>";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_INVALID_METHOD);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 2);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, v11_POST_no_len_has_type_with_body)
|
||||
{
|
||||
/*
|
||||
* v1.1 POST
|
||||
* if no obvious content-length header, the body will be parsed as new header,
|
||||
* and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-type: text/html\r\n\r\n<html>some content<html/>";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_INVALID_METHOD);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 2);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, v10_POST_no_len_has_type_with_body)
|
||||
{
|
||||
/*
|
||||
* v1.0 POST
|
||||
* if no obvious content-length header, the body will be parsed as new header,
|
||||
* and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error!
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *request = "POST /path/index.html HTTP/1.0\r\nHost: example.com\r\nContent-type: text/html\r\n\r\n<html>some content<html/>";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request));
|
||||
ASSERT_EQ(lerr, HPE_CLOSED_CONNECTION);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_url, 1);
|
||||
ASSERT_EQ(para.count.flag_on_method, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
}
|
||||
|
||||
/******************************** response test case *********************************/
|
||||
TEST(HTTP_llhttp, response_base)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nhello,world";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_status, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 1);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_dir_error)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nhello,world";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_TRUE(lerr != HPE_OK);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_uncompleted)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nxx";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_status, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 0);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_hdr_pipeline)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 3);
|
||||
ASSERT_EQ(para.count.flag_on_status, 3);
|
||||
ASSERT_EQ(para.count.flag_on_version, 3);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 3);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 3);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_hdr_body_pipeline)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 1\r\n\r\nxHTTP/1.1 200 OK\r\nServer: nginx2\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\nxxHTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 3\r\n\r\nxxx";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 3);
|
||||
ASSERT_EQ(para.count.flag_on_status, 3);
|
||||
ASSERT_EQ(para.count.flag_on_version, 3);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 3);
|
||||
ASSERT_EQ(para.count.flag_on_body, 3);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 3);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_no_len_no_type_body)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\n\r\nxxx";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_status, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_no_len_has_type_body)
|
||||
{
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\n\r\nxxx";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_status, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_no_len_no_type_pipeline)
|
||||
{
|
||||
/*
|
||||
* if no obvious content-length header, the body will be parsed until connection cloesd!
|
||||
*
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_status, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 1);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 0);
|
||||
}
|
||||
|
||||
TEST(HTTP_llhttp, response_no_len_no_type_and_reset)
|
||||
{
|
||||
/*
|
||||
* if no obvious content-length header, the body will be parsed until connection cloesd!
|
||||
*
|
||||
*/
|
||||
struct gtest_http_parm para = {};
|
||||
gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings);
|
||||
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n";
|
||||
enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
|
||||
lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
|
||||
lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
|
||||
llhttp_reset(¶.llhttp_parser);
|
||||
memset(¶.count, 0, sizeof(para.count));
|
||||
response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n";
|
||||
|
||||
lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response));
|
||||
ASSERT_EQ(lerr, HPE_OK);
|
||||
|
||||
ASSERT_EQ(para.count.flag_on_message_begin, 1);
|
||||
ASSERT_EQ(para.count.flag_on_status, 1);
|
||||
ASSERT_EQ(para.count.flag_on_version, 1);
|
||||
ASSERT_EQ(para.count.flag_on_headers_complete, 1);
|
||||
ASSERT_EQ(para.count.flag_on_body, 0);
|
||||
ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed if no content-length filed
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, (char **)argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
78
test/monitor/CMakeLists.txt
Normal file
78
test/monitor/CMakeLists.txt
Normal file
@@ -0,0 +1,78 @@
|
||||
cmake_minimum_required (VERSION 2.8...3.10)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include/)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/infra/)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/)
|
||||
|
||||
set(MONITOR_SRC_DIR ${CMAKE_SOURCE_DIR}/infra/monitor)
|
||||
# set(MONITOR_DEPEND_LIB gtest pcap sds linenoise pthread toml libevent-static cjson-static fieldstat4 stellar_lib monitor)
|
||||
set(MONITOR_DEPEND_LIB gtest pcap pthread fieldstat4 )
|
||||
set(WHOLE_ARCHIVE_STATIC_LIB sds linenoise toml libevent-static cjson-static stellar_lib monitor session_manager tuple)
|
||||
|
||||
add_executable(gtest_stm_sds gtest_sds.cpp)
|
||||
add_executable(gtest_stm_util gtest_monitor_util.cpp )
|
||||
# add_executable(gtest_stm_packet_dump_unit gtest_packet_dump_unit.cpp)
|
||||
# add_executable(gtest_stm_packet_dump gtest_packet_dump.cpp)
|
||||
add_executable(gtest_cmd_assistant gtest_cmd_assistant.cpp )
|
||||
add_executable(gtest_stm_spinlock gtest_spinlock.cpp )
|
||||
# add_executable(gtest_seek_layer gtest_seek_layer.cpp)
|
||||
add_executable(gtest_stm_server gtest_monitor_server.cpp)
|
||||
add_executable(gtest_stm_topk gtest_topk.cpp)
|
||||
add_executable(stm_server monitor_main.cpp)
|
||||
add_executable(gtest_stm_rpc gtest_rpc.cpp)
|
||||
set(MONITOR_TEST_FILE gtest_stm_sds gtest_stm_util gtest_cmd_assistant gtest_stm_spinlock gtest_stm_server gtest_stm_topk stm_server gtest_stm_rpc)
|
||||
foreach(tfile ${MONITOR_TEST_FILE})
|
||||
target_link_libraries(${tfile} ${MONITOR_DEPEND_LIB} -Wl,--whole-archive ${WHOLE_ARCHIVE_STATIC_LIB} -Wl,--no-whole-archive)
|
||||
endforeach()
|
||||
|
||||
set(MONITOR_TEST_RUN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_test(NAME MONITOR_ENV_SETUP COMMAND sh -c "mkdir -p ${MONITOR_TEST_RUN_DIR}/conf &&
|
||||
mkdir -p ${MONITOR_TEST_RUN_DIR}/plugin &&
|
||||
mkdir -p ${MONITOR_TEST_RUN_DIR}/log &&
|
||||
mkdir -p ${MONITOR_TEST_RUN_DIR}/pcap &&
|
||||
cp ${CMAKE_SOURCE_DIR}/conf/stellar.toml ${MONITOR_TEST_RUN_DIR}/conf/stellar.toml &&
|
||||
tomlq -t -i '.packet_io.mode=\"pcapfile\"' ${MONITOR_TEST_RUN_DIR}/conf/stellar.toml &&
|
||||
tomlq -t -i '.packet_io.pcap_path=\"pcap/test.pcap\"' ${MONITOR_TEST_RUN_DIR}/conf/stellar.toml &&
|
||||
rm -f ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
|
||||
echo '[log]' >> ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
|
||||
echo 'output = \"file\"' >> ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
|
||||
echo 'level = \"DEBUG\"' >> ${MONITOR_TEST_RUN_DIR}/conf/log.toml &&
|
||||
rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
cp ${CMAKE_BINARY_DIR}/tools/monitor/stellar-cli ${MONITOR_TEST_RUN_DIR}/stellar-cli &&
|
||||
cp ${CMAKE_BINARY_DIR}/tools/monitor/tcpdump/src/tcpdump-build/stellar-dump ${MONITOR_TEST_RUN_DIR}/stellar-dump")
|
||||
|
||||
add_test(NAME MONITOR_COPY_PCAP COMMAND sh -c "rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
cp ${CMAKE_SOURCE_DIR}/test/monitor/pcap/monitor_benchmark.pcap ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
md5sum ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap |grep 79d4c8e526f638b59b12b7defb5021d9")
|
||||
|
||||
add_test(NAME MONITOR_TEST_SERVER COMMAND sh -c "./gtest_stm_server" WORKING_DIRECTORY ${GMONITOR_TEST_RUN_DIR})
|
||||
set_tests_properties(MONITOR_ENV_SETUP
|
||||
PROPERTIES FIXTURES_SETUP STM_ENV_SETUP)
|
||||
set_tests_properties(MONITOR_TEST_SERVER
|
||||
PROPERTIES FIXTURES_REQUIRED STM_ENV_SETUP)
|
||||
|
||||
# add_test(NAME MONITOR_PKT_DUMP_COPY_PCAP COMMAND sh -c "rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
# cp ${CMAKE_SOURCE_DIR}/test/monitor/pcap/monitor_packet_dump.pcap ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
# md5sum ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap |grep eb7795612a389d71d0f82fef5737981d")
|
||||
# set_tests_properties(MONITOR_ENV_SETUP MONITOR_PKT_DUMP_COPY_PCAP
|
||||
# PROPERTIES FIXTURES_SETUP STM_GTEST_PKT_DUMP)
|
||||
# add_test(NAME MONITOR_TEST_PKT_DUMP COMMAND sh -c "./gtest_stm_packet_dump" WORKING_DIRECTORY ${MONITOR_TEST_RUN_DIR})
|
||||
# set_tests_properties(MONITOR_TEST_PKT_DUMP
|
||||
# PROPERTIES FIXTURES_REQUIRED STM_GTEST_PKT_DUMP)
|
||||
|
||||
# add_test(NAME MONITOR_PKT_DUMP_TUNNEL_COPY_PCAP COMMAND sh -c "rm -f ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
# cp ${CMAKE_SOURCE_DIR}/test/monitor/pcap/monitor_tunnel.pcap ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap &&
|
||||
# md5sum ${MONITOR_TEST_RUN_DIR}/pcap/test.pcap |grep 525dab2a04d02b36d7d21be16f51e1cd")
|
||||
# add_executable(gtest_stm_packet_dump_tunnel gtest_packet_dump_tunnel.cpp ${STM_DEP_SRC} )
|
||||
# target_link_libraries(gtest_stm_packet_dump_tunnel ${MONITOR_DEPEND_LIB} ${WHOLE_ARCHIVE_STATIC_LIB} )
|
||||
|
||||
# set_tests_properties(MONITOR_ENV_SETUP MONITOR_PKT_DUMP_TUNNEL_COPY_PCAP
|
||||
# PROPERTIES FIXTURES_SETUP STM_GTEST_PKT_DUMP_TUNNEL)
|
||||
# add_test(NAME MONITOR_TEST_PKT_DUMP_TUNNEL COMMAND sh -c "./gtest_stm_packet_dump_tunnel" WORKING_DIRECTORY ${MONITOR_TEST_RUN_DIR})
|
||||
# set_tests_properties(MONITOR_TEST_PKT_DUMP_TUNNEL
|
||||
# PROPERTIES FIXTURES_REQUIRED STM_GTEST_PKT_DUMP_TUNNEL)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(gtest_stm_util)
|
||||
gtest_discover_tests(gtest_cmd_assistant)
|
||||
# gtest_discover_tests(gtest_seek_layer)
|
||||
252
test/monitor/gtest_cmd_assistant.cpp
Normal file
252
test/monitor/gtest_cmd_assistant.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "monitor/monitor_cmd_assistant.h"
|
||||
#include "sds/sds.h"
|
||||
#include "linenoise/linenoise.h"
|
||||
|
||||
static struct monitor_reply *test_cmd_cb(int argc, char *argv[], void *user_arg)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
(void)user_arg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST(MONITOR_CMD_ASSISTANT, sds_compare)
|
||||
{
|
||||
int cli_argc, register_cmd_argc;
|
||||
const char *cli = "show";
|
||||
const char *register_cmd = "show";
|
||||
sds *cli_array = sdssplitargs(cli, &cli_argc);
|
||||
sds *register_cmd_array = sdssplitargs(register_cmd, ®ister_cmd_argc);
|
||||
EXPECT_EQ(0, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
|
||||
sdsfreesplitres(cli_array, cli_argc);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_argc);
|
||||
|
||||
cli = "s";
|
||||
register_cmd = "show";
|
||||
cli_array = sdssplitargs(cli, &cli_argc);
|
||||
register_cmd_array = sdssplitargs(register_cmd, ®ister_cmd_argc);
|
||||
EXPECT_EQ(-1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
|
||||
sdsfreesplitres(cli_array, cli_argc);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_argc);
|
||||
|
||||
cli = "show";
|
||||
register_cmd = "show session";
|
||||
cli_array = sdssplitargs(cli, &cli_argc);
|
||||
register_cmd_array = sdssplitargs(register_cmd, ®ister_cmd_argc);
|
||||
EXPECT_EQ(-1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
|
||||
sdsfreesplitres(cli_array, cli_argc);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_argc);
|
||||
|
||||
cli = "show s";
|
||||
register_cmd = "show session";
|
||||
cli_array = sdssplitargs(cli, &cli_argc);
|
||||
register_cmd_array = sdssplitargs(register_cmd, ®ister_cmd_argc);
|
||||
EXPECT_EQ(-1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
|
||||
sdsfreesplitres(cli_array, cli_argc);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_argc);
|
||||
|
||||
cli = "show session xxx yyy zzz";
|
||||
register_cmd = "show session";
|
||||
cli_array = sdssplitargs(cli, &cli_argc);
|
||||
register_cmd_array = sdssplitargs(register_cmd, ®ister_cmd_argc);
|
||||
EXPECT_EQ(1, stm_cmd_assistant_sds_compare(cli_array, cli_argc, register_cmd_array, register_cmd_argc));
|
||||
sdsfreesplitres(cli_array, cli_argc);
|
||||
sdsfreesplitres(register_cmd_array, register_cmd_argc);
|
||||
}
|
||||
|
||||
TEST(MONITOR_CMD_ASSISTANT, cmd_register)
|
||||
{
|
||||
struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)"test_cmd_cb", (void *)"get args", "readonly", "key", "get xxx");
|
||||
EXPECT_EQ((void *)"test_cmd_cb", stm_cmd_assistant_get_cb(cmd_aide, "get"));
|
||||
EXPECT_EQ((void *)"get args", stm_cmd_assistant_get_user_arg(cmd_aide, "get"));
|
||||
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show session", (void *)"show_session_cb", (void *)"show session args", "readonly", "id", "show session xxx");
|
||||
EXPECT_EQ((void *)"show_session_cb", stm_cmd_assistant_get_cb(cmd_aide, "show session id 1"));
|
||||
EXPECT_EQ((void *)"show session args", stm_cmd_assistant_get_user_arg(cmd_aide, "show session id 3"));
|
||||
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show http", (void *)"show_http_cb", (void *)"show http args", "readonly", "id", "show http xxx");
|
||||
EXPECT_EQ((void *)"show_http_cb", stm_cmd_assistant_get_cb(cmd_aide, "show http url a.b.com"));
|
||||
EXPECT_EQ((void *)"show http args", stm_cmd_assistant_get_user_arg(cmd_aide, "show http url a.b.com"));
|
||||
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show http stat", (void *)"show_http_stat_cb", (void *)"show http stat args", "readonly", "id", "show http stat xxx");
|
||||
EXPECT_EQ((void *)"show_http_stat_cb", stm_cmd_assistant_get_cb(cmd_aide, "show http stat brief"));
|
||||
EXPECT_EQ((void *)"show http stat args", stm_cmd_assistant_get_user_arg(cmd_aide, "show http stat brief"));
|
||||
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show http stat brief limit", (void *)"show_http_stat_limit_cb", (void *)"show http stat limit args", "readonly", "id", "show http stat limit xxx");
|
||||
EXPECT_EQ((void *)"show_http_stat_limit_cb", stm_cmd_assistant_get_cb(cmd_aide, "show http stat brief limit 1"));
|
||||
EXPECT_EQ((void *)"show http stat limit args", stm_cmd_assistant_get_user_arg(cmd_aide, "show http stat brief limit 1"));
|
||||
|
||||
stm_cmd_assistant_free(cmd_aide);
|
||||
}
|
||||
|
||||
TEST(MONITOR_CMD_ASSISTANT, serialize_dserialize)
|
||||
{
|
||||
struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)test_cmd_cb, NULL, "readonly", "key", "get xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "set", (void *)test_cmd_cb, NULL, "write", "key value [NX] [EX seconds | PX milliseconds]", "set xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show session id", (void *)test_cmd_cb, NULL, "readonly", "value", "show session id xxx");
|
||||
|
||||
char *free_after_used = stm_cmd_assistant_serialize(cmd_aide);
|
||||
printf("serialize : \n%s\n", free_after_used);
|
||||
|
||||
struct stm_cmd_assistant *cmd_aide_new = stm_cmd_assistant_new();
|
||||
stm_cmd_assistant_dserialize(cmd_aide_new, free_after_used);
|
||||
char *free_after_used2 = stm_cmd_assistant_serialize(cmd_aide_new);
|
||||
printf("serialize : \n%s\n", free_after_used2);
|
||||
ASSERT_STREQ(free_after_used, free_after_used2);
|
||||
|
||||
free(free_after_used);
|
||||
free(free_after_used2);
|
||||
stm_cmd_assistant_free(cmd_aide);
|
||||
stm_cmd_assistant_free(cmd_aide_new);
|
||||
}
|
||||
|
||||
struct gtest_cmd_completion_stat
|
||||
{
|
||||
linenoiseCompletions *lc;
|
||||
int expect_completion_num;
|
||||
int match_num;
|
||||
int not_match_num;
|
||||
const char *expect_completion_str[128];
|
||||
};
|
||||
struct gtest_cmd_completion_stat g_expect_stat = {};
|
||||
|
||||
static void gtest_cmd_completion_cb(void *arg, const char *candidate_completion)
|
||||
{
|
||||
struct gtest_cmd_completion_stat *local_expect_stat = (struct gtest_cmd_completion_stat *)arg;
|
||||
for (int i = 0; i < local_expect_stat->expect_completion_num; i++)
|
||||
{
|
||||
if (0 == strcasecmp(local_expect_stat->expect_completion_str[i], candidate_completion))
|
||||
{
|
||||
// printf("gtest completion cb match : %s\n", candidate_completion);
|
||||
local_expect_stat->match_num++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
local_expect_stat->not_match_num++;
|
||||
}
|
||||
|
||||
void gtest_linenoise_completion_cb(const char *line, linenoiseCompletions *lc)
|
||||
{
|
||||
g_expect_stat.lc = lc;
|
||||
stm_cmd_assistant_input_line(stm_cmd_assistant_get(), line, (void *)&g_expect_stat);
|
||||
}
|
||||
|
||||
TEST(MONITOR_CMD_ASSISTANT, cmd_completion)
|
||||
{
|
||||
struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)test_cmd_cb, NULL, "readonly", "key", "get xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "set", (void *)test_cmd_cb, NULL, "write", "key value [NX] [EX seconds | PX milliseconds]", "set xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show session", (void *)test_cmd_cb, NULL, "readonly", "value", "show session xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show session id", (void *)test_cmd_cb, NULL, "readonly", "value", "show session id xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show http", (void *)test_cmd_cb, NULL, "readonly", "status ", "show http xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show ssl", (void *)test_cmd_cb, NULL, "readonly", "status", "show ssl xxx");
|
||||
|
||||
g_expect_stat.expect_completion_num = 4;
|
||||
g_expect_stat.expect_completion_str[0] = "show session";
|
||||
g_expect_stat.expect_completion_str[1] = "show session id";
|
||||
g_expect_stat.expect_completion_str[2] = "show http";
|
||||
g_expect_stat.expect_completion_str[3] = "show ssl";
|
||||
|
||||
stm_cmd_assistant_set_completion_cb(cmd_aide, gtest_cmd_completion_cb);
|
||||
|
||||
#if 0 // linenoise not call completion callback if input is not stdin (freopn stdin to a local file)
|
||||
linenoiseSetCompletionCallback(gtest_linenoise_completion_cb);
|
||||
FILE *fake_stdin_file = fopen("./__fake_stdin_file", "w+");
|
||||
fprintf(fake_stdin_file, "show");
|
||||
fflush(fake_stdin_file);
|
||||
fclose(fake_stdin_file);
|
||||
FILE *raw_stdin_ptr = stdin;
|
||||
ASSERT_TRUE(freopen("./__fake_stdin_file", "r", stdin) != NULL);
|
||||
|
||||
g_expect_stat.not_match_num = 0;
|
||||
g_expect_stat.match_num = 0;
|
||||
char *line = linenoise("cli> ");
|
||||
printf("fake cli input line : %s\n", line);
|
||||
free(line);
|
||||
stdin = raw_stdin_ptr;
|
||||
#else
|
||||
(void)stm_cmd_assistant_input_line(cmd_aide, "show", &g_expect_stat);
|
||||
#endif
|
||||
EXPECT_EQ(0, g_expect_stat.not_match_num);
|
||||
EXPECT_EQ(g_expect_stat.match_num, g_expect_stat.expect_completion_num);
|
||||
|
||||
g_expect_stat.not_match_num = 0;
|
||||
g_expect_stat.match_num = 0;
|
||||
stm_cmd_assistant_input_line(cmd_aide, "sh", &g_expect_stat);
|
||||
EXPECT_EQ(0, g_expect_stat.not_match_num);
|
||||
EXPECT_EQ(g_expect_stat.match_num, g_expect_stat.expect_completion_num);
|
||||
|
||||
g_expect_stat.not_match_num = 0;
|
||||
g_expect_stat.match_num = 0;
|
||||
stm_cmd_assistant_input_line(cmd_aide, "show h", &g_expect_stat);
|
||||
EXPECT_EQ(0, g_expect_stat.not_match_num);
|
||||
EXPECT_EQ(1, g_expect_stat.match_num);
|
||||
|
||||
g_expect_stat.not_match_num = 0;
|
||||
g_expect_stat.match_num = 0;
|
||||
stm_cmd_assistant_input_line(cmd_aide, "show s", &g_expect_stat);
|
||||
EXPECT_EQ(0, g_expect_stat.not_match_num);
|
||||
EXPECT_EQ(3, g_expect_stat.match_num);
|
||||
|
||||
g_expect_stat.not_match_num = 0;
|
||||
g_expect_stat.match_num = 0;
|
||||
stm_cmd_assistant_input_line(cmd_aide, "showxxx", &g_expect_stat);
|
||||
EXPECT_EQ(0, g_expect_stat.match_num);
|
||||
|
||||
stm_cmd_assistant_free(cmd_aide);
|
||||
}
|
||||
|
||||
char *line_noise_hints_callback(const char *line, int *color, int *bold)
|
||||
{
|
||||
char *hints = (char *)stm_cmd_assistant_input_line_for_hints(stm_cmd_assistant_get(), line);
|
||||
// all commands hints result preappend a blank space before hints
|
||||
if (NULL == hints)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
sds tmp = sdsnew(" ");
|
||||
tmp = sdscat(tmp, hints);
|
||||
*color = 90;
|
||||
*bold = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void line_noise_free_hints_callback(void *arg)
|
||||
{
|
||||
sdsfree((sds)arg);
|
||||
}
|
||||
|
||||
TEST(MONITOR_CMD_ASSISTANT, cmd_hints)
|
||||
{
|
||||
struct stm_cmd_assistant *cmd_aide = stm_cmd_assistant_new();
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "get", (void *)test_cmd_cb, NULL, "readonly", "key", "get xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "set", (void *)test_cmd_cb, NULL, "write", "key value [NX] [EX seconds | PX milliseconds]", "set xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show session", (void *)test_cmd_cb, NULL, "readonly", "value", "show session");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show session id", (void *)test_cmd_cb, NULL, "readonly", "id", "show session id xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show http", (void *)test_cmd_cb, NULL, "readonly", "status", "show http xxx");
|
||||
stm_cmd_assistant_register_cmd(cmd_aide, "show ssl", (void *)test_cmd_cb, NULL, "readonly", "status", "show ssl xxx");
|
||||
|
||||
EXPECT_STREQ("key", stm_cmd_assistant_input_line_for_hints(cmd_aide, "get"));
|
||||
EXPECT_STREQ("key value [NX] [EX seconds | PX milliseconds]", stm_cmd_assistant_input_line_for_hints(cmd_aide, "set"));
|
||||
EXPECT_STREQ(NULL, stm_cmd_assistant_input_line_for_hints(cmd_aide, "xxx"));
|
||||
EXPECT_STREQ(NULL, stm_cmd_assistant_input_line_for_hints(cmd_aide, "show"));
|
||||
EXPECT_STREQ("value", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show session"));
|
||||
EXPECT_STREQ("id", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show session id"));
|
||||
EXPECT_STREQ("status", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show http"));
|
||||
EXPECT_STREQ("status", stm_cmd_assistant_input_line_for_hints(cmd_aide, "show ssl"));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
172
test/monitor/gtest_monitor_server.cpp
Normal file
172
test/monitor/gtest_monitor_server.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <gtest/gtest.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "stellar/monitor.h"
|
||||
#include "stellar/stellar.h"
|
||||
#include "monitor/monitor_private.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
static sds *stellar_cli_exec_cmd(const char *command_str, int *result_size)
|
||||
{
|
||||
int ret;
|
||||
sds *cmd_result_array = NULL;
|
||||
char result[4096] = {};
|
||||
FILE *fp = popen(command_str, "r");
|
||||
if (NULL == fp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ret = fread(result, 1, sizeof(result), fp);
|
||||
if (ret > 0)
|
||||
{
|
||||
cmd_result_array = sdssplitlen(result, strlen(result), "\r\n", 1, result_size);
|
||||
}
|
||||
pclose(fp);
|
||||
return cmd_result_array;
|
||||
}
|
||||
|
||||
#if 0 // for TEST_F
|
||||
class MonitorServerMock : public testing::Test
|
||||
{
|
||||
public:
|
||||
static struct stellar *st;
|
||||
static pthread_t tid;
|
||||
static int thread_count;
|
||||
static int cmd_result_line_num;
|
||||
static int cmd_array_size;
|
||||
static sds *cmd_result_array;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
printf("Gtest Stm Server: Setup Test Case Env...\n");
|
||||
st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
}
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
printf("Gtest Stm Server: Tear Down Test Case Env...\n");
|
||||
stellar_free(st);
|
||||
}
|
||||
};
|
||||
pthread_t MonitorServerMock::tid;
|
||||
int MonitorServerMock::thread_count;
|
||||
int MonitorServerMock::cmd_result_line_num;
|
||||
struct stellar *MonitorServerMock::st;
|
||||
int MonitorServerMock::cmd_array_size;
|
||||
sds *MonitorServerMock::cmd_result_array;
|
||||
#endif
|
||||
|
||||
TEST(monitor_server, curl_ping)
|
||||
{
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
cmd_result_array = stellar_cli_exec_cmd("curl --silent http://127.0.0.1:80/v1/stellar_monitor?raw_cmd=ping", &cmd_array_size);
|
||||
ASSERT_TRUE(cmd_array_size >= 1);
|
||||
ASSERT_TRUE(cmd_result_array != NULL);
|
||||
EXPECT_STREQ("pong", cmd_result_array[0]);
|
||||
sdsfreesplitres(cmd_result_array, cmd_array_size);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(monitor_server, curl_ping_messge)
|
||||
{
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
cmd_result_array = stellar_cli_exec_cmd("curl --silent http://127.0.0.1:80/v1/stellar_monitor?raw_cmd=ping%20hello,world", &cmd_array_size);
|
||||
ASSERT_TRUE(cmd_array_size >= 1);
|
||||
ASSERT_TRUE(cmd_result_array != NULL);
|
||||
EXPECT_STREQ("hello,world", cmd_result_array[0]);
|
||||
sdsfreesplitres(cmd_result_array, cmd_array_size);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(monitor_server, stellar_cli_ping)
|
||||
{
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e ping", &cmd_array_size);
|
||||
ASSERT_TRUE(cmd_array_size >= 1);
|
||||
ASSERT_TRUE(cmd_result_array != NULL);
|
||||
EXPECT_STREQ("pong", cmd_result_array[0]);
|
||||
sdsfreesplitres(cmd_result_array, cmd_array_size);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(monitor_server, stellar_cli_ping_messge)
|
||||
{
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e ping hello,world", &cmd_array_size);
|
||||
ASSERT_TRUE(cmd_array_size >= 1);
|
||||
ASSERT_TRUE(cmd_result_array != NULL);
|
||||
EXPECT_STREQ("hello,world", cmd_result_array[0]);
|
||||
sdsfreesplitres(cmd_result_array, cmd_array_size);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(monitor_server, not_found)
|
||||
{
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e xxxxxxx", &cmd_array_size);
|
||||
ASSERT_TRUE(cmd_array_size >= 1);
|
||||
ASSERT_TRUE(cmd_result_array != NULL);
|
||||
printf("cmd_result_array[0]: %s\n", cmd_result_array[0]);
|
||||
EXPECT_TRUE(strstr(cmd_result_array[0], "ERR unknown command") != NULL);
|
||||
sdsfreesplitres(cmd_result_array, cmd_array_size);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(monitor_server, invalid_args)
|
||||
{
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st != NULL);
|
||||
stellar_run(st);
|
||||
cmd_result_array = stellar_cli_exec_cmd("./stellar-cli -e ping x y z ", &cmd_array_size);
|
||||
ASSERT_TRUE(cmd_array_size >= 1);
|
||||
ASSERT_TRUE(cmd_result_array != NULL);
|
||||
printf("cmd_result_array[0]: %s\n", cmd_result_array[0]);
|
||||
EXPECT_TRUE(strstr(cmd_result_array[0], "ERR wrong number of arguments") != NULL);
|
||||
sdsfreesplitres(cmd_result_array, cmd_array_size);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
453
test/monitor/gtest_monitor_util.cpp
Normal file
453
test/monitor/gtest_monitor_util.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "monitor/monitor_utils.h"
|
||||
#include "sds/sds.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
TEST(MONITOR_UTIL, key_value_test)
|
||||
{
|
||||
struct stm_key_value *kv = NULL;
|
||||
char key_buf[16];
|
||||
char value_buf[16];
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
snprintf(key_buf, sizeof(key_buf), "key%d", i);
|
||||
snprintf(value_buf, sizeof(value_buf), "value%d", i);
|
||||
stm_cmd_key_value_append(&kv, key_buf, value_buf);
|
||||
}
|
||||
|
||||
ASSERT_EQ(10, kv->tuple_num);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
snprintf(key_buf, sizeof(key_buf), "key%d", i);
|
||||
snprintf(value_buf, sizeof(value_buf), "value%d", i);
|
||||
ASSERT_STREQ(key_buf, kv->tuple[i].key);
|
||||
ASSERT_STREQ(value_buf, kv->tuple[i].value);
|
||||
}
|
||||
|
||||
stm_cmd_key_value_free(kv);
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, stm_strncasecmp_exactly)
|
||||
{
|
||||
ASSERT_EQ(0, stm_strncasecmp_exactly("abc", "abc", 3));
|
||||
ASSERT_EQ(0, stm_strncasecmp_exactly("abc", "ABC", 3));
|
||||
ASSERT_EQ(-1, stm_strncasecmp_exactly("abc", "ABCD", 3));
|
||||
ASSERT_EQ(-1, stm_strncasecmp_exactly("abc", "ABCD", 4));
|
||||
ASSERT_EQ(-1, stm_strncasecmp_exactly(NULL, "ABCD", 4));
|
||||
ASSERT_EQ(-1, stm_strncasecmp_exactly("abc", NULL, 4));
|
||||
ASSERT_EQ(-1, stm_strncasecmp_exactly("", NULL, 4));
|
||||
ASSERT_EQ(0, stm_strncasecmp_exactly("", "", 0));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, time_range)
|
||||
{
|
||||
time_t time_range[2];
|
||||
ASSERT_EQ(-1, stm_time_range_pton(NULL, 0, NULL));
|
||||
ASSERT_EQ(-1, stm_time_range_pton(NULL, 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-7-days", 0, NULL));
|
||||
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last", 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-", 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-1", 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-1-", 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-1-d", 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-1-da", 0, time_range));
|
||||
ASSERT_EQ(-1, stm_time_range_pton("last-1-day", 0, time_range));
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-days", 0, time_range));
|
||||
|
||||
time_t now = 10000000;
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-seconds", now, time_range));
|
||||
ASSERT_EQ(now - 1, time_range[0]);
|
||||
ASSERT_EQ(now, time_range[1]);
|
||||
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-minutes", now, time_range));
|
||||
ASSERT_EQ(now - 60, time_range[0]);
|
||||
ASSERT_EQ(now, time_range[1]);
|
||||
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-hours", now, time_range));
|
||||
ASSERT_EQ(now - 60 * 60, time_range[0]);
|
||||
ASSERT_EQ(now, time_range[1]);
|
||||
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-days", now, time_range));
|
||||
ASSERT_EQ(now - 24 * 60 * 60, time_range[0]);
|
||||
ASSERT_EQ(now, time_range[1]);
|
||||
|
||||
now = 100;
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-999-days", now, time_range));
|
||||
ASSERT_EQ(0, time_range[0]);
|
||||
ASSERT_EQ(now, time_range[1]);
|
||||
|
||||
now = 10000;
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-seconds", now, time_range));
|
||||
ASSERT_EQ(1, stm_time_in_range(now - 1, time_range));
|
||||
ASSERT_EQ(0, stm_time_in_range(now - 10, time_range));
|
||||
ASSERT_EQ(0, stm_time_in_range(now + 1, time_range));
|
||||
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-minutes", now, time_range));
|
||||
ASSERT_EQ(1, stm_time_in_range(now - 60, time_range));
|
||||
ASSERT_EQ(0, stm_time_in_range(now - 61, time_range));
|
||||
ASSERT_EQ(0, stm_time_in_range(now + 1, time_range));
|
||||
|
||||
ASSERT_EQ(0, stm_time_range_pton("last-1-hours", now, time_range));
|
||||
ASSERT_EQ(1, stm_time_in_range(now - 60 * 60, time_range));
|
||||
ASSERT_EQ(0, stm_time_in_range(now - 60 * 60 - 1, time_range));
|
||||
ASSERT_EQ(0, stm_time_in_range(now + 1, time_range));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, inet_pton)
|
||||
{
|
||||
unsigned int ipv4;
|
||||
struct in6_addr ipv6;
|
||||
|
||||
ASSERT_EQ(0, stm_inet_pton(NULL, NULL, NULL));
|
||||
ASSERT_EQ(0, stm_inet_pton("", NULL, NULL));
|
||||
ASSERT_EQ(0, stm_inet_pton("1.1.1.1", NULL, &ipv6));
|
||||
ASSERT_EQ(0, stm_inet_pton("1234::1", &ipv4, NULL));
|
||||
|
||||
ASSERT_EQ(AF_INET, stm_inet_pton("1.2.3.4", &ipv4, &ipv6));
|
||||
ASSERT_EQ(0x04030201, ipv4);
|
||||
|
||||
ASSERT_EQ(AF_INET, stm_inet_pton("1.2.3.0", &ipv4, &ipv6));
|
||||
ASSERT_EQ(0x00030201, ipv4);
|
||||
|
||||
struct in6_addr ipv6_2;
|
||||
inet_pton(AF_INET6, "1234::1", &ipv6_2);
|
||||
ASSERT_EQ(AF_INET6, stm_inet_pton("1234::1", &ipv4, &ipv6));
|
||||
ASSERT_EQ(0, memcmp(&ipv6_2, &ipv6, sizeof(struct in6_addr)));
|
||||
|
||||
inet_pton(AF_INET6, "1234::0", &ipv6_2);
|
||||
ASSERT_EQ(AF_INET6, stm_inet_pton("1234::0", &ipv4, &ipv6));
|
||||
ASSERT_EQ(0, memcmp(&ipv6_2, &ipv6, sizeof(struct in6_addr)));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, ip_cidr_pton)
|
||||
{
|
||||
unsigned int ipv4, maskv4;
|
||||
struct in6_addr ipv6, maskv6;
|
||||
struct in6_addr expect_maskv6;
|
||||
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton(NULL, NULL, NULL, NULL, NULL));
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton("abcdefg", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/xx", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/8", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0xFF000000, ntohl(maskv4));
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/16", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0xFFFF0000, ntohl(maskv4));
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/24", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0xFFFFFF00, ntohl(maskv4));
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.1/32", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
ASSERT_EQ(0xFFFFFFFF, maskv4);
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/33", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
|
||||
/* IPV6 */
|
||||
ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("::1/8", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
memset(&expect_maskv6, 0, sizeof(struct in6_addr));
|
||||
expect_maskv6.s6_addr[0] = 0xFF;
|
||||
ASSERT_EQ(0, memcmp(&expect_maskv6, &maskv6, sizeof(struct in6_addr)));
|
||||
|
||||
ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("::1/32", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
memset(&expect_maskv6, 0, sizeof(struct in6_addr));
|
||||
expect_maskv6.s6_addr[0] = 0xFF;
|
||||
expect_maskv6.s6_addr[1] = 0xFF;
|
||||
expect_maskv6.s6_addr[2] = 0xFF;
|
||||
expect_maskv6.s6_addr[3] = 0xFF;
|
||||
ASSERT_EQ(0, memcmp(&expect_maskv6, &maskv6, sizeof(struct in6_addr)));
|
||||
|
||||
ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("::1/128", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
memset(&expect_maskv6, 0xFF, sizeof(struct in6_addr));
|
||||
ASSERT_EQ(0, memcmp(&expect_maskv6, &maskv6, sizeof(struct in6_addr)));
|
||||
|
||||
ASSERT_EQ(0, stm_ip_cidr_pton("1.1.1.1/129", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, stm_ipv4_cidr_to_range)
|
||||
{
|
||||
uint32_t ipaddr, ipmask;
|
||||
uint32_t iprange[2];
|
||||
inet_pton(AF_INET, "192.168.1.1", &ipaddr);
|
||||
inet_pton(AF_INET, "255.0.0.0", &ipmask);
|
||||
stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
|
||||
ASSERT_EQ(iprange[0], ntohl(0xc0000000));
|
||||
ASSERT_EQ(iprange[1], ntohl(0xc0FFFFFF));
|
||||
|
||||
inet_pton(AF_INET, "192.168.1.1", &ipaddr);
|
||||
inet_pton(AF_INET, "255.255.0.0", &ipmask);
|
||||
stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
|
||||
ASSERT_EQ(iprange[0], ntohl(0xc0A80000));
|
||||
ASSERT_EQ(iprange[1], ntohl(0xc0A8FFFF));
|
||||
|
||||
inet_pton(AF_INET, "192.168.1.1", &ipaddr);
|
||||
inet_pton(AF_INET, "255.128.0.0", &ipmask);
|
||||
stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
|
||||
ASSERT_EQ(iprange[0], ntohl(0xc0800000));
|
||||
ASSERT_EQ(iprange[1], ntohl(0xc0FFFFFF));
|
||||
|
||||
inet_pton(AF_INET, "192.168.1.1", &ipaddr);
|
||||
inet_pton(AF_INET, "255.255.255.0", &ipmask);
|
||||
stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
|
||||
ASSERT_EQ(iprange[0], ntohl(0xc0A80100));
|
||||
ASSERT_EQ(iprange[1], ntohl(0xc0A801FF));
|
||||
|
||||
inet_pton(AF_INET, "192.168.1.1", &ipaddr);
|
||||
inet_pton(AF_INET, "255.255.255.255", &ipmask);
|
||||
stm_ipv4_cidr_to_range(ipaddr, ipmask, iprange);
|
||||
ASSERT_EQ(iprange[0], ntohl(0xc0A80101));
|
||||
ASSERT_EQ(iprange[1], ntohl(0xc0A80101));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, stm_ipv4_cidr_string_to_range)
|
||||
{
|
||||
uint32_t ipv4, maskv4;
|
||||
struct in6_addr ipv6, maskv6;
|
||||
uint32_t ipv4_range[2];
|
||||
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.0/8", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
stm_ipv4_cidr_to_range(ipv4, maskv4, ipv4_range);
|
||||
ASSERT_EQ(ipv4_range[0], ntohl(0x01000000));
|
||||
ASSERT_EQ(ipv4_range[1], ntohl(0x01FFFFFF));
|
||||
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.1.1.0/24", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
stm_ipv4_cidr_to_range(ipv4, maskv4, ipv4_range);
|
||||
ASSERT_EQ(ipv4_range[0], ntohl(0x01010100));
|
||||
ASSERT_EQ(ipv4_range[1], ntohl(0x010101FF));
|
||||
|
||||
ASSERT_EQ(AF_INET, stm_ip_cidr_pton("1.2.3.4/32", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
stm_ipv4_cidr_to_range(ipv4, maskv4, ipv4_range);
|
||||
ASSERT_EQ(ipv4_range[0], ntohl(0x01020304));
|
||||
ASSERT_EQ(ipv4_range[1], ntohl(0x01020304));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, stm_ipv6_cidr_string_to_range)
|
||||
{
|
||||
uint32_t ipv4, maskv4;
|
||||
struct in6_addr ipv6, maskv6;
|
||||
struct in6_addr ipv6_range[2];
|
||||
struct in6_addr expect_addr;
|
||||
|
||||
ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("1234::abcd/8", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
stm_ipv6_cidr_to_range(&ipv6, &maskv6, ipv6_range);
|
||||
inet_pton(AF_INET6, "1200::0", &expect_addr);
|
||||
ASSERT_EQ(0, memcmp(&ipv6_range[0], &expect_addr, sizeof(struct in6_addr)));
|
||||
inet_pton(AF_INET6, "12ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_addr);
|
||||
ASSERT_EQ(0, memcmp(&ipv6_range[1], &expect_addr, sizeof(struct in6_addr)));
|
||||
|
||||
ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("1234::abcd/16", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
stm_ipv6_cidr_to_range(&ipv6, &maskv6, ipv6_range);
|
||||
inet_pton(AF_INET6, "1234::0", &expect_addr);
|
||||
ASSERT_EQ(0, memcmp(&ipv6_range[0], &expect_addr, sizeof(struct in6_addr)));
|
||||
inet_pton(AF_INET6, "1234:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_addr);
|
||||
ASSERT_EQ(0, memcmp(&ipv6_range[1], &expect_addr, sizeof(struct in6_addr)));
|
||||
|
||||
ASSERT_EQ(AF_INET6, stm_ip_cidr_pton("1234::abcd/128", &ipv4, &maskv4, &ipv6, &maskv6));
|
||||
stm_ipv6_cidr_to_range(&ipv6, &maskv6, ipv6_range);
|
||||
inet_pton(AF_INET6, "1234::abcd", &expect_addr);
|
||||
ASSERT_EQ(0, memcmp(&ipv6_range[0], &expect_addr, sizeof(struct in6_addr)));
|
||||
ASSERT_EQ(0, memcmp(&ipv6_range[1], &expect_addr, sizeof(struct in6_addr)));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, stm_ipv6_cidr_to_range)
|
||||
{
|
||||
struct in6_addr ipaddr, ipmask;
|
||||
struct in6_addr iprange[2];
|
||||
struct in6_addr expect_range[2];
|
||||
|
||||
inet_pton(AF_INET6, "1234::abcd", &ipaddr);
|
||||
inet_pton(AF_INET6, "ff00::", &ipmask); // 8bit mask
|
||||
stm_ipv6_cidr_to_range(&ipaddr, &ipmask, iprange);
|
||||
inet_pton(AF_INET6, "1200::", &expect_range[0]);
|
||||
inet_pton(AF_INET6, "12ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_range[1]);
|
||||
ASSERT_EQ(0, memcmp(&expect_range[0], &iprange[0], sizeof(struct in6_addr)));
|
||||
ASSERT_EQ(0, memcmp(&expect_range[1], &iprange[1], sizeof(struct in6_addr)));
|
||||
|
||||
inet_pton(AF_INET6, "1234::abcd", &ipaddr);
|
||||
inet_pton(AF_INET6, "ffff::", &ipmask); // 16bit mask
|
||||
stm_ipv6_cidr_to_range(&ipaddr, &ipmask, iprange);
|
||||
inet_pton(AF_INET6, "1234::", &expect_range[0]);
|
||||
inet_pton(AF_INET6, "1234:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &expect_range[1]);
|
||||
ASSERT_EQ(0, memcmp(&expect_range[0], &iprange[0], sizeof(struct in6_addr)));
|
||||
ASSERT_EQ(0, memcmp(&expect_range[1], &iprange[1], sizeof(struct in6_addr)));
|
||||
|
||||
inet_pton(AF_INET6, "1234::abcd", &ipaddr);
|
||||
inet_pton(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &ipmask); // 128bit mask
|
||||
stm_ipv6_cidr_to_range(&ipaddr, &ipmask, iprange);
|
||||
inet_pton(AF_INET6, "1234::abcd", &expect_range[0]);
|
||||
inet_pton(AF_INET6, "1234::abcd", &expect_range[1]);
|
||||
ASSERT_EQ(0, memcmp(&expect_range[0], &iprange[0], sizeof(struct in6_addr)));
|
||||
ASSERT_EQ(0, memcmp(&expect_range[1], &iprange[1], sizeof(struct in6_addr)));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, mem_rand)
|
||||
{
|
||||
char buf[10000];
|
||||
srand(time(NULL));
|
||||
stm_mem_fill_rand(buf, sizeof(buf), 31, 61);
|
||||
for (size_t i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
ASSERT_GE(buf[i], 31);
|
||||
ASSERT_LE(buf[i], 61);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, timeout)
|
||||
{
|
||||
struct timeval start, end;
|
||||
start.tv_sec = 10000;
|
||||
start.tv_usec = 0;
|
||||
|
||||
end = start;
|
||||
ASSERT_EQ(0, stm_timeout(start, end, 1));
|
||||
end.tv_sec += 1;
|
||||
ASSERT_EQ(1, stm_timeout(start, end, 1));
|
||||
|
||||
end = start;
|
||||
end.tv_usec += 999999;
|
||||
ASSERT_EQ(0, stm_timeout(start, end, 1));
|
||||
|
||||
start.tv_sec = 10000;
|
||||
start.tv_usec = 900 * 1000;
|
||||
end.tv_sec = 10001;
|
||||
end.tv_usec = 1;
|
||||
ASSERT_EQ(0, stm_timeout(start, end, 1));
|
||||
end.tv_usec += 999999;
|
||||
ASSERT_EQ(1, stm_timeout(start, end, 1));
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, parse_cli_arg)
|
||||
{
|
||||
struct monitor_cli_args expect_args[] =
|
||||
{
|
||||
{"-i", "--ip", 1, 0, NULL},
|
||||
{"-p", "--port", 1, 0, NULL},
|
||||
{"-t", "--timeout", 1, 0, NULL},
|
||||
{"-e", "--exec", 1, 1, NULL},
|
||||
};
|
||||
|
||||
int argc = 0;
|
||||
const char *cli_cmd = NULL;
|
||||
sds *argv = NULL;
|
||||
|
||||
/* TEST: short options */
|
||||
cli_cmd = "./monitor_cli -i 1.1.1.1 -p 8080 -t 30 -e a b c d e f g ";
|
||||
argv = sdssplitargs((char *)cli_cmd, &argc);
|
||||
EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
|
||||
EXPECT_STREQ("1.1.1.1", expect_args[0].value);
|
||||
EXPECT_STREQ("8080", expect_args[1].value);
|
||||
EXPECT_STREQ("30", expect_args[2].value);
|
||||
EXPECT_STREQ("a b c d e f g", expect_args[3].value);
|
||||
sdsfree(expect_args[0].value);
|
||||
expect_args[0].value = NULL;
|
||||
sdsfree(expect_args[1].value);
|
||||
expect_args[1].value = NULL;
|
||||
sdsfree(expect_args[2].value);
|
||||
expect_args[2].value = NULL;
|
||||
sdsfree(expect_args[3].value);
|
||||
expect_args[3].value = NULL;
|
||||
sdsfreesplitres(argv, argc);
|
||||
|
||||
/* TEST: long options */
|
||||
cli_cmd = "./monitor_cli --ip 111.111.111.111 --port 80808 --timeout 300 --exec a b c d e f g ";
|
||||
argv = sdssplitargs((char *)cli_cmd, &argc);
|
||||
EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
|
||||
EXPECT_STREQ("111.111.111.111", expect_args[0].value);
|
||||
EXPECT_STREQ("80808", expect_args[1].value);
|
||||
EXPECT_STREQ("300", expect_args[2].value);
|
||||
EXPECT_STREQ("a b c d e f g", expect_args[3].value);
|
||||
sdsfree(expect_args[0].value);
|
||||
expect_args[0].value = NULL;
|
||||
sdsfree(expect_args[1].value);
|
||||
expect_args[1].value = NULL;
|
||||
sdsfree(expect_args[2].value);
|
||||
expect_args[2].value = NULL;
|
||||
sdsfree(expect_args[3].value);
|
||||
expect_args[3].value = NULL;
|
||||
sdsfreesplitres(argv, argc);
|
||||
|
||||
/* TEST: short options out of order */
|
||||
cli_cmd = "./monitor_cli -e a b c d e f g -t 30 -i 1.1.1.1 -p 8080 ";
|
||||
argv = sdssplitargs((char *)cli_cmd, &argc);
|
||||
EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
|
||||
EXPECT_STREQ("1.1.1.1", expect_args[0].value);
|
||||
EXPECT_STREQ("8080", expect_args[1].value);
|
||||
EXPECT_STREQ("30", expect_args[2].value);
|
||||
EXPECT_STREQ("a b c d e f g", expect_args[3].value);
|
||||
sdsfree(expect_args[0].value);
|
||||
expect_args[0].value = NULL;
|
||||
sdsfree(expect_args[1].value);
|
||||
expect_args[1].value = NULL;
|
||||
sdsfree(expect_args[2].value);
|
||||
expect_args[2].value = NULL;
|
||||
sdsfree(expect_args[3].value);
|
||||
expect_args[3].value = NULL;
|
||||
sdsfreesplitres(argv, argc);
|
||||
|
||||
/* TEST: short options with quotes */
|
||||
cli_cmd = "./monitor_cli -i 1.1.1.1 -p 8080 -t 30 -e \"a b c d e f g\"";
|
||||
argv = sdssplitargs((char *)cli_cmd, &argc);
|
||||
EXPECT_EQ(0, monitor_util_parse_cmd_args(argc, (const char **)argv, expect_args, 4));
|
||||
EXPECT_STREQ("1.1.1.1", expect_args[0].value);
|
||||
EXPECT_STREQ("8080", expect_args[1].value);
|
||||
EXPECT_STREQ("30", expect_args[2].value);
|
||||
EXPECT_STREQ("a b c d e f g", expect_args[3].value);
|
||||
sdsfree(expect_args[0].value);
|
||||
expect_args[0].value = NULL;
|
||||
sdsfree(expect_args[1].value);
|
||||
expect_args[1].value = NULL;
|
||||
sdsfree(expect_args[2].value);
|
||||
expect_args[2].value = NULL;
|
||||
sdsfree(expect_args[3].value);
|
||||
expect_args[3].value = NULL;
|
||||
sdsfreesplitres(argv, argc);
|
||||
}
|
||||
|
||||
TEST(MONITOR_UTIL, reply)
|
||||
{
|
||||
struct monitor_reply *reply;
|
||||
|
||||
reply = monitor_reply_nil();
|
||||
sds reply_str = monitor_reply_to_string(reply);
|
||||
EXPECT_STREQ(reply_str, "(nil)\r\n");
|
||||
sdsfree(reply_str);
|
||||
monitor_reply_free(reply);
|
||||
|
||||
reply = monitor_reply_new_string("hello, %s", "world");
|
||||
reply_str = monitor_reply_to_string(reply);
|
||||
EXPECT_STREQ(reply_str, "hello, world\r\n");
|
||||
sdsfree(reply_str);
|
||||
monitor_reply_free(reply);
|
||||
|
||||
reply = monitor_reply_new_integer(12345);
|
||||
reply_str = monitor_reply_to_string(reply);
|
||||
EXPECT_STREQ(reply_str, "(integer) 12345\r\n");
|
||||
sdsfree(reply_str);
|
||||
monitor_reply_free(reply);
|
||||
|
||||
reply = monitor_reply_new_double(123.456);
|
||||
reply_str = monitor_reply_to_string(reply);
|
||||
EXPECT_STREQ(reply_str, "(double) 123.456000\r\n");
|
||||
sdsfree(reply_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
276
test/monitor/gtest_packet_dump.cpp
Normal file
276
test/monitor/gtest_packet_dump.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pcap/pcap.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "stellar/stellar.h"
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "sds/sds.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TEST_PKT_DUMP_FILE_NAME "__gtest_stm_pkt_dump.pcap"
|
||||
|
||||
struct async_cmd_args
|
||||
{
|
||||
pthread_t tid;
|
||||
const char *cmd;
|
||||
int cmd_array_size;
|
||||
sds *cmd_result_array;
|
||||
int wait_thread_create_time_ms;
|
||||
};
|
||||
|
||||
static sds *stellar_cli_exec_cmd(const char *command_str, int *result_size)
|
||||
{
|
||||
int ret;
|
||||
sds *cmd_result_array = NULL;
|
||||
char result[4096] = {};
|
||||
FILE *fp = popen(command_str, "r");
|
||||
if (NULL == fp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ret = fread(result, 1, sizeof(result), fp);
|
||||
if (ret > 0)
|
||||
{
|
||||
cmd_result_array = sdssplitlen(result, strlen(result), "\r\n", 1, result_size);
|
||||
}
|
||||
pclose(fp);
|
||||
return cmd_result_array;
|
||||
}
|
||||
|
||||
static void *async_cmd_run_thread(void *arg)
|
||||
{
|
||||
struct async_cmd_args *async_arg = (struct async_cmd_args *)arg;
|
||||
async_arg->cmd_result_array = stellar_cli_exec_cmd(async_arg->cmd, &async_arg->cmd_array_size);
|
||||
return NULL;
|
||||
}
|
||||
static void async_cmd_run(struct async_cmd_args *arg)
|
||||
{
|
||||
pthread_create(&arg->tid, NULL, async_cmd_run_thread, arg);
|
||||
usleep(arg->wait_thread_create_time_ms * 1000);
|
||||
}
|
||||
static void async_cmd_wait(struct async_cmd_args *async_arg)
|
||||
{
|
||||
pthread_join(async_arg->tid, NULL);
|
||||
}
|
||||
|
||||
// class MonitorServerMock : public testing::Test
|
||||
// {
|
||||
// public:
|
||||
// static struct stellar *st;
|
||||
// static pthread_t tid;
|
||||
// static int thread_count;
|
||||
// static int cmd_result_line_num;
|
||||
// static int cmd_array_size;
|
||||
// static sds *cmd_result_array;
|
||||
|
||||
// protected:
|
||||
// static void SetUpTestCase()
|
||||
// {
|
||||
// printf("Gtest Stm Server: Setup Test Case Env...\n");
|
||||
// st = stellar_new("./conf/stellar.toml", "./plugin/spec.toml", "./conf/log.toml");
|
||||
// assert(st != NULL);
|
||||
// }
|
||||
// static void TearDownTestCase()
|
||||
// {
|
||||
// printf("Gtest Stm Server: Tear Down Test Case Env...\n");
|
||||
// stellar_free(st);
|
||||
// }
|
||||
// };
|
||||
// pthread_t MonitorServerMock::tid;
|
||||
// int MonitorServerMock::thread_count;
|
||||
// int MonitorServerMock::cmd_result_line_num;
|
||||
// struct stellar *MonitorServerMock::st;
|
||||
// int MonitorServerMock::cmd_array_size;
|
||||
// sds *MonitorServerMock::cmd_result_array;
|
||||
|
||||
static int get_local_pcap_packet_number(const char *filename)
|
||||
{
|
||||
char cmd_buf[256] = {};
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "tcpdump -r %s -n -nn -t -q | wc -l", filename);
|
||||
int cmd_res_size;
|
||||
sds *cmd_res = stellar_cli_exec_cmd(cmd_buf, &cmd_res_size);
|
||||
|
||||
int pkt_num = 0;
|
||||
for (int i = 0; i < cmd_res_size; i++)
|
||||
{
|
||||
if (strstr(cmd_res[i], "reading from file") != NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (strstr(cmd_res[i], "tcpdump") != NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (isdigit(cmd_res[i][0]))
|
||||
{
|
||||
pkt_num = atoi(cmd_res[i]);
|
||||
}
|
||||
}
|
||||
sdsfreesplitres(cmd_res, cmd_res_size);
|
||||
return pkt_num;
|
||||
}
|
||||
|
||||
static int get_netstat_num(const char *cmd)
|
||||
{
|
||||
int cmd_res_size;
|
||||
sds *cmd_res = stellar_cli_exec_cmd(cmd, &cmd_res_size);
|
||||
|
||||
int pkt_num = 0;
|
||||
for (int i = 0; i < cmd_res_size; i++)
|
||||
{
|
||||
if (isdigit(cmd_res[i][0]))
|
||||
{
|
||||
pkt_num = atoi(cmd_res[i]);
|
||||
}
|
||||
}
|
||||
sdsfreesplitres(cmd_res, cmd_res_size);
|
||||
return pkt_num;
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP, base)
|
||||
{
|
||||
struct async_cmd_args async_arg = {};
|
||||
async_arg.wait_thread_create_time_ms = 100;
|
||||
char cmd_buf[256] = {};
|
||||
remove(TEST_PKT_DUMP_FILE_NAME);
|
||||
const int expect_cap_pkt_num = 11;
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
ASSERT_TRUE(st != NULL);
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d -s0 -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
|
||||
async_arg.cmd = cmd_buf;
|
||||
async_cmd_run(&async_arg);
|
||||
stellar_run(st);
|
||||
async_cmd_wait(&async_arg);
|
||||
sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
|
||||
|
||||
int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
|
||||
EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP, arg_ip_port)
|
||||
{
|
||||
struct async_cmd_args async_arg = {};
|
||||
async_arg.wait_thread_create_time_ms = 100;
|
||||
char cmd_buf[256] = {};
|
||||
remove(TEST_PKT_DUMP_FILE_NAME);
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
ASSERT_TRUE(st != NULL);
|
||||
|
||||
const int expect_cap_pkt_num = 13;
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d -i 127.0.0.1 -P 80 -s0 -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
|
||||
async_arg.cmd = cmd_buf;
|
||||
async_cmd_run(&async_arg);
|
||||
stellar_run(st);
|
||||
async_cmd_wait(&async_arg);
|
||||
sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
|
||||
|
||||
int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
|
||||
EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP, arg_bpf)
|
||||
{
|
||||
struct async_cmd_args async_arg = {};
|
||||
async_arg.wait_thread_create_time_ms = 100;
|
||||
char cmd_buf[256] = {};
|
||||
remove(TEST_PKT_DUMP_FILE_NAME);
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
ASSERT_TRUE(st != NULL);
|
||||
|
||||
const int expect_cap_pkt_num = 33;
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d host 172.16.0.203 and host 172.100.15.25 and udp port 1935 and udp port 52043 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
|
||||
async_arg.cmd = cmd_buf;
|
||||
async_cmd_run(&async_arg);
|
||||
stellar_run(st);
|
||||
async_cmd_wait(&async_arg);
|
||||
sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
|
||||
|
||||
int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
|
||||
EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP, all_args)
|
||||
{
|
||||
struct async_cmd_args async_arg = {};
|
||||
async_arg.wait_thread_create_time_ms = 100;
|
||||
char cmd_buf[256] = {};
|
||||
remove(TEST_PKT_DUMP_FILE_NAME);
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
ASSERT_TRUE(st != NULL);
|
||||
|
||||
const int expect_cap_pkt_num = 33;
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -c %d -i 127.0.0.1 -P 80 -g host 172.16.0.203 and host 172.100.15.25 and udp port 1935 and udp port 52043 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
|
||||
async_arg.cmd = cmd_buf;
|
||||
async_cmd_run(&async_arg);
|
||||
stellar_run(st);
|
||||
async_cmd_wait(&async_arg);
|
||||
sdsfreesplitres(async_arg.cmd_result_array, async_arg.cmd_array_size);
|
||||
|
||||
int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
|
||||
EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP, too_many_clients)
|
||||
{
|
||||
#define MAX_CLIENT_NUM 1000
|
||||
struct async_cmd_args async_arg[MAX_CLIENT_NUM] = {};
|
||||
char cmd_buf[256] = {};
|
||||
const int expect_cap_pkt_num = 1;
|
||||
remove(TEST_PKT_DUMP_FILE_NAME);
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
ASSERT_TRUE(st != NULL);
|
||||
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -q -t -c 1 udp port 5005 and udp port 39627 -w /dev/null"); // the last udp packet
|
||||
|
||||
for (int i = 0; i < MAX_CLIENT_NUM; i++)
|
||||
{
|
||||
async_arg[i].wait_thread_create_time_ms = 0;
|
||||
async_arg[i].cmd = cmd_buf;
|
||||
async_cmd_run(&async_arg[i]);
|
||||
}
|
||||
struct async_cmd_args save_pcap_async_arg = {};
|
||||
save_pcap_async_arg.wait_thread_create_time_ms = 100;
|
||||
char save_pcap_cmd_buf[256] = {};
|
||||
snprintf(save_pcap_cmd_buf, sizeof(save_pcap_cmd_buf), "./stellar-dump -n -nn -q -t -c %d udp port 5005 and udp port 39627 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
|
||||
save_pcap_async_arg.cmd = save_pcap_cmd_buf;
|
||||
async_cmd_run(&save_pcap_async_arg);
|
||||
|
||||
/* todo: how do I make sure all stellar-dump are started? */
|
||||
while (get_netstat_num("netstat -anup | grep stellar | wc -l") < MAX_CLIENT_NUM + 1)
|
||||
{
|
||||
printf("### wait for all stellar-dump starting...\n");
|
||||
sleep(1);
|
||||
}
|
||||
stellar_run(st);
|
||||
|
||||
for (int i = 0; i < MAX_CLIENT_NUM; i++)
|
||||
{
|
||||
async_cmd_wait(&async_arg[i]);
|
||||
sdsfreesplitres(async_arg[i].cmd_result_array, async_arg[i].cmd_array_size);
|
||||
}
|
||||
async_cmd_wait(&save_pcap_async_arg);
|
||||
sdsfreesplitres(save_pcap_async_arg.cmd_result_array, save_pcap_async_arg.cmd_array_size);
|
||||
|
||||
int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
|
||||
EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
140
test/monitor/gtest_packet_dump_tunnel.cpp
Normal file
140
test/monitor/gtest_packet_dump_tunnel.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pcap/pcap.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "sds/sds.h"
|
||||
#include "stellar/stellar.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TEST_PKT_DUMP_FILE_NAME "__gtest_stm_pkt_dump.pcap"
|
||||
static pthread_t tid;
|
||||
static int cmd_result_array_size;
|
||||
static sds *cmd_result_array;
|
||||
|
||||
static sds *stellar_cli_exec_cmd(const char *command_str, int *result_size)
|
||||
{
|
||||
int ret;
|
||||
sds *cmd_res_array = NULL;
|
||||
char result[4096] = {};
|
||||
FILE *fp = popen(command_str, "r");
|
||||
if (NULL == fp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ret = fread(result, 1, sizeof(result), fp);
|
||||
if (ret > 0)
|
||||
{
|
||||
cmd_res_array = sdssplitlen(result, strlen(result), "\r\n", 1, result_size);
|
||||
}
|
||||
pclose(fp);
|
||||
return cmd_res_array;
|
||||
}
|
||||
|
||||
|
||||
static void *async_cmd_run_thread(void *arg)
|
||||
{
|
||||
cmd_result_array = stellar_cli_exec_cmd((char *)arg, &cmd_result_array_size);
|
||||
return NULL;
|
||||
}
|
||||
static void async_cmd_run(char *cmd)
|
||||
{
|
||||
pthread_create(&tid, NULL, async_cmd_run_thread, cmd);
|
||||
usleep(100000);
|
||||
}
|
||||
static void async_cmd_wait(void)
|
||||
{
|
||||
pthread_join(tid, NULL);
|
||||
}
|
||||
|
||||
// class MonitorServerMock : public testing::Test
|
||||
// {
|
||||
// public:
|
||||
// static struct stellar *st;
|
||||
// static pthread_t tid;
|
||||
// static int thread_count;
|
||||
// static int cmd_result_line_num;
|
||||
// static int cmd_result_array_size;
|
||||
// static sds *cmd_result_array;
|
||||
|
||||
// protected:
|
||||
// static void SetUpTestCase()
|
||||
// {
|
||||
// printf("Gtest Stm Server: Setup Test Case Env...\n");
|
||||
// st = stellar_new("./conf/stellar.toml");
|
||||
// assert(st != NULL);
|
||||
// }
|
||||
// static void TearDownTestCase()
|
||||
// {
|
||||
// printf("Gtest Stm Server: Tear Down Test Case Env...\n");
|
||||
// stellar_free(st);
|
||||
// }
|
||||
// };
|
||||
// pthread_t MonitorServerMock::tid;
|
||||
// int MonitorServerMock::thread_count;
|
||||
// int MonitorServerMock::cmd_result_line_num;
|
||||
// struct stellar *MonitorServerMock::st;
|
||||
// int MonitorServerMock::cmd_result_array_size;
|
||||
// sds *MonitorServerMock::cmd_result_array;
|
||||
|
||||
static int get_local_pcap_packet_number(const char *filename)
|
||||
{
|
||||
char cmd_buf[256] = {};
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "tcpdump -r %s -n -nn -t -q | wc -l", filename);
|
||||
int cmd_res_size;
|
||||
sds *cmd_res = stellar_cli_exec_cmd(cmd_buf, &cmd_res_size);
|
||||
|
||||
int pkt_num = 0;
|
||||
for (int i = 0; i < cmd_res_size; i++)
|
||||
{
|
||||
if (strstr(cmd_res[i], "reading from file") != NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (strstr(cmd_res[i], "tcpdump") != NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (isdigit(cmd_res[i][0]))
|
||||
{
|
||||
pkt_num = atoi(cmd_res[i]);
|
||||
}
|
||||
}
|
||||
sdsfreesplitres(cmd_res, cmd_res_size);
|
||||
return pkt_num;
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_TUNNEL, arg_bpf)
|
||||
{
|
||||
char cmd_buf[256] = {};
|
||||
remove(TEST_PKT_DUMP_FILE_NAME);
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
ASSERT_TRUE(st != NULL);
|
||||
|
||||
const int expect_cap_pkt_num = 5;
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "./stellar-dump -n -nn -g -c %d udp port 53 or udp port 443 or udp port 8246 or udp port 6620 -s0 -U -w %s", expect_cap_pkt_num, TEST_PKT_DUMP_FILE_NAME);
|
||||
async_cmd_run(cmd_buf);
|
||||
stellar_run(st);
|
||||
async_cmd_wait();
|
||||
sdsfreesplitres(cmd_result_array, cmd_result_array_size);
|
||||
|
||||
int actual_cap_num = get_local_pcap_packet_number(TEST_PKT_DUMP_FILE_NAME);
|
||||
EXPECT_EQ(expect_cap_pkt_num, actual_cap_num);
|
||||
stellar_free(st);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
229
test/monitor/gtest_packet_dump_unit.cpp
Normal file
229
test/monitor/gtest_packet_dump_unit.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pcap/pcap.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "stellar/packet.h"
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "sds/sds.h"
|
||||
#include "monitor/monitor_packet_dump.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TEST_PKT_DUMP_FILE_NAME "__gtest_stm_pkt_dump.pcap"
|
||||
|
||||
/* mock */
|
||||
int packet_manager_subscribe(UNUSED struct packet_manager *pkt_mgr, UNUSED enum packet_stage stage, UNUSED on_packet_stage_callback *cb, UNUSED void *args)
|
||||
{
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "greedy", "threads", "1,2,3", "datalinkip", "1.2.3.4", "datalinkport", "54321", "bpf", "tcp", "port", "80"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_EQ(NULL, reply);
|
||||
EXPECT_EQ(1, tmp_conn_parm.greedy_mode);
|
||||
EXPECT_EQ(0, thread_enable_flag[0]);
|
||||
EXPECT_EQ(1, thread_enable_flag[1]);
|
||||
EXPECT_EQ(1, thread_enable_flag[2]);
|
||||
EXPECT_EQ(1, thread_enable_flag[3]);
|
||||
EXPECT_EQ(0, thread_enable_flag[5]);
|
||||
EXPECT_EQ(0, thread_enable_flag[7]);
|
||||
EXPECT_EQ(0, thread_enable_flag[9]);
|
||||
EXPECT_EQ(0x04030201, tmp_conn_parm.peer_data_ip_net_order);
|
||||
EXPECT_EQ(htons(54321), tmp_conn_parm.peer_data_port_net_order);
|
||||
EXPECT_STREQ(cli_bpf_str, "tcp port 80");
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args_out_of_order)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "54321", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_EQ(NULL, reply);
|
||||
EXPECT_EQ(1, tmp_conn_parm.greedy_mode);
|
||||
EXPECT_EQ(0, thread_enable_flag[0]);
|
||||
EXPECT_EQ(1, thread_enable_flag[1]);
|
||||
EXPECT_EQ(0, thread_enable_flag[2]);
|
||||
EXPECT_EQ(1, thread_enable_flag[3]);
|
||||
EXPECT_EQ(1, thread_enable_flag[5]);
|
||||
EXPECT_EQ(1, thread_enable_flag[7]);
|
||||
EXPECT_EQ(1, thread_enable_flag[9]);
|
||||
EXPECT_EQ(0, thread_enable_flag[10]);
|
||||
EXPECT_EQ(0x04030201, tmp_conn_parm.peer_data_ip_net_order);
|
||||
EXPECT_EQ(htons(54321), tmp_conn_parm.peer_data_port_net_order);
|
||||
EXPECT_STREQ(cli_bpf_str, "tcp port 80 ");
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_ip)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.256", "datalinkport", "54321", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str = NULL;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_TRUE(reply != NULL);
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_port)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "654321", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str = NULL;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_TRUE(reply != NULL);
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args_no_port)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "bpf", "tcp", "port", "80", "greedy", "threads", "1,3,5,7,9"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str = NULL;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_TRUE(reply != NULL);
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_bpf)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "654321", "bpf", "tcp", "port", "xxx", "greedy", "threads", "1,3,5,7,9"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str = NULL;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_TRUE(reply != NULL);
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, parse_args_error_threads)
|
||||
{
|
||||
int thread_enable_flag[STELLAR_MAX_THREAD_NUM] = {};
|
||||
struct stm_pktdump_task tmp_conn_parm = {};
|
||||
const char *argv[] = {"./stellar_dump", "datalinkip", "1.2.3.4", "datalinkport", "54321", "bpf", "tcp", "port", "xxx", "greedy", "threads", "1,3,5,7,999"};
|
||||
int argc = sizeof(argv) / sizeof(char *);
|
||||
sds cli_bpf_str = NULL;
|
||||
struct monitor_reply *reply = stm_packet_dump_parse_args(argc, (char **)argv, &tmp_conn_parm, thread_enable_flag, STELLAR_MAX_THREAD_NUM, &cli_bpf_str);
|
||||
ASSERT_TRUE(reply != NULL);
|
||||
pcap_freecode(&tmp_conn_parm.bpf_filter);
|
||||
sdsfree(cli_bpf_str);
|
||||
monitor_reply_free(reply);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, pcap_compile_and_filter_ipv4)
|
||||
{
|
||||
/* This is a IPv4 tcp SYN packet, 192.168.40.139:48662 -> 107.155.25.121:80 */
|
||||
static const unsigned char packet_bytes[] = {
|
||||
0x48, 0x73, 0x97, 0x96, 0x38, 0x10, 0x00, 0x22,
|
||||
0x46, 0x2f, 0x35, 0xb4, 0x08, 0x00,
|
||||
0x45, 0x00, 0x00, 0x38, 0x0c, 0x1d, 0x40, 0x00, 0x40, 0x06,
|
||||
0xc0, 0x5b, 0xc0, 0xa8, 0x28, 0x8b, 0x6b, 0x9b,
|
||||
0x19, 0x79, 0xbe, 0x16, 0x00, 0x50, 0x7b, 0xf9,
|
||||
0x8b, 0x34, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02,
|
||||
0x72, 0x10, 0x6e, 0x72, 0x00, 0x00, 0x02, 0x04,
|
||||
0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x2d, 0xbb,
|
||||
0x87, 0x29, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
struct bpf_program bpf_bin;
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 48662 and host 192.168.40.139 and host 107.155.25.121", 1, 0);
|
||||
int match_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
|
||||
EXPECT_TRUE(match_from_eth != 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 12345 and tcp port 54321", 1, 0);
|
||||
int unmatch_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
|
||||
EXPECT_TRUE(unmatch_from_eth == 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "tcp port 48662 and host 192.168.40.139 and host 107.155.25.121", 1, 0);
|
||||
int match_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
|
||||
EXPECT_TRUE(match_from_ip != 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "host 1.2.3.4 and host 5.6.7.8", 1, 0);
|
||||
int unmatch_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
|
||||
EXPECT_TRUE(unmatch_from_ip == 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
}
|
||||
|
||||
TEST(MONITOR_PKT_DUMP_UNIT, pcap_compile_and_filter_ipv6)
|
||||
{
|
||||
/* This is a IPv6 tcp SYN packet, 2001::192.168.40.134:37948 -> 2001::192.168.40.133:22 */
|
||||
static const unsigned char packet_bytes[] = {
|
||||
0x00, 0x22, 0x46, 0x36, 0x51, 0x38, 0x00, 0x22,
|
||||
0x46, 0x36, 0x51, 0x3c, 0x86, 0xdd, 0x60, 0x00,
|
||||
0x00, 0x00, 0x00, 0x28, 0x06, 0x40, 0x20, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92,
|
||||
0x01, 0x68, 0x00, 0x40, 0x01, 0x34, 0x20, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92,
|
||||
0x01, 0x68, 0x00, 0x40, 0x01, 0x33, 0x94, 0x3c,
|
||||
0x00, 0x16, 0x09, 0x68, 0x7d, 0x85, 0x00, 0x00,
|
||||
0x00, 0x00, 0xa0, 0x02, 0x71, 0xac, 0xff, 0xec,
|
||||
0x00, 0x00, 0x02, 0x04, 0x07, 0x94, 0x04, 0x02,
|
||||
0x08, 0x0a, 0x16, 0xa8, 0x59, 0xc2, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x03, 0x03, 0x07};
|
||||
|
||||
struct bpf_program bpf_bin;
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 37948 and host 2001::192.168:40:134 and host 2001::192:168:40:133", 1, 0);
|
||||
int match_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
|
||||
EXPECT_TRUE(match_from_eth != 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_EN10MB, &bpf_bin, "tcp port 12345 and tcp port 54321", 1, 0);
|
||||
int unmatch_from_eth = bpf_filter(bpf_bin.bf_insns, packet_bytes, sizeof(packet_bytes), sizeof(packet_bytes));
|
||||
EXPECT_TRUE(unmatch_from_eth == 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "tcp port 37948 and host 2001::192.168:40:134 and host 2001::192:168:40:133", 1, 0);
|
||||
int match_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
|
||||
EXPECT_TRUE(match_from_ip != 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(STM_PACKET_DUMP_CAP_LEN, DLT_RAW, &bpf_bin, "host 1.2.3.4 and host 5.6.7.8", 1, 0);
|
||||
int unmatch_from_ip = bpf_filter(bpf_bin.bf_insns, packet_bytes + 14, sizeof(packet_bytes) - 14, sizeof(packet_bytes) - 14);
|
||||
EXPECT_TRUE(unmatch_from_ip == 0);
|
||||
pcap_freecode(&bpf_bin);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
194
test/monitor/gtest_ringbuf.cpp
Normal file
194
test/monitor/gtest_ringbuf.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <pthread.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "stellar/monitor.h"
|
||||
#include "monitor_ringbuf.h"
|
||||
|
||||
#define TOTAL_TEST_NUM 100000
|
||||
#define BATCH_NUM 10
|
||||
#define MAX_PAYLOAD_LEN 2048
|
||||
#define MAGIC_NUM 0x1234ABCD
|
||||
static struct monitor_ringbuf_wrap *rbf_ctop;
|
||||
static struct monitor_ringbuf_wrap *rbf_ptoc;
|
||||
static const size_t ringbuf_size = MAX_PAYLOAD_LEN * 1024;
|
||||
|
||||
static unsigned long long ctop_producer_len = 0, ctop_consumer_len = 0;
|
||||
static unsigned long long ptoc_producer_len = 0, ptoc_consumer_len = 0;
|
||||
|
||||
struct test_ringbuf_data
|
||||
{
|
||||
long long magic;
|
||||
long long block_len; /* total length, include this header and paylaod */
|
||||
char payload[MAX_PAYLOAD_LEN];
|
||||
};
|
||||
|
||||
static int test_ringbuf_push_mode = 0; /* 0: easy push, 1: push by offset */
|
||||
|
||||
static int ringbuf_push(int tid, struct monitor_ringbuf_wrap *rbf, struct test_ringbuf_data *payload)
|
||||
{
|
||||
int ret = 0;
|
||||
if (0 == test_ringbuf_push_mode)
|
||||
{
|
||||
ret = stm_ringbuf_easy_push(tid, rbf, payload, payload->block_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssize_t offset = stm_ringbuf_stream_start(tid, rbf, payload->block_len);
|
||||
if (offset < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
stm_ringbuf_stream_append(tid, rbf, offset++, payload++, 1);
|
||||
usleep(1);
|
||||
stm_ringbuf_stream_append(tid, rbf, offset++, payload++, 1);
|
||||
usleep(10);
|
||||
stm_ringbuf_stream_append(tid, rbf, offset++, payload++, 1);
|
||||
usleep(100);
|
||||
stm_ringbuf_stream_append(tid, rbf, offset, payload, payload->block_len - 3);
|
||||
stm_ringbuf_stream_finish(tid, rbf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ringbuf_producer(int tid, struct monitor_ringbuf_wrap *rbf, int batch)
|
||||
{
|
||||
struct test_ringbuf_data t;
|
||||
for (int i = 0; i < batch; i++)
|
||||
{
|
||||
t.magic = MAGIC_NUM;
|
||||
t.block_len = rand() % (MAX_PAYLOAD_LEN - sizeof(long long) * 2) + sizeof(long long) * 2; /* variable-length */
|
||||
while (ringbuf_push(tid, rbf, &t) < 0)
|
||||
{
|
||||
usleep(1);
|
||||
}
|
||||
if (rbf == rbf_ctop)
|
||||
{
|
||||
ctop_producer_len += t.block_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptoc_producer_len += t.block_len;
|
||||
}
|
||||
}
|
||||
return batch;
|
||||
}
|
||||
|
||||
static int ringbuf_customer(int tid, struct monitor_ringbuf_wrap *rbf, int batch)
|
||||
{
|
||||
(void)tid;
|
||||
void *data;
|
||||
size_t pop_len;
|
||||
for (int i = 0; i < batch; i++)
|
||||
{
|
||||
while ((data = stm_ringbuf_pop(rbf, &pop_len)) == NULL)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
struct test_ringbuf_data *td = (struct test_ringbuf_data *)data;
|
||||
if (td->magic != MAGIC_NUM)
|
||||
{
|
||||
stm_ringbuf_release(rbf, pop_len); /* maybe lost many messages */
|
||||
return 0;
|
||||
}
|
||||
td->magic = random();
|
||||
size_t rel_len = td->block_len;
|
||||
if (rbf == rbf_ctop)
|
||||
{
|
||||
ctop_consumer_len += td->block_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptoc_consumer_len += td->block_len;
|
||||
}
|
||||
td->block_len = 0;
|
||||
stm_ringbuf_release(rbf, rel_len);
|
||||
}
|
||||
return batch;
|
||||
}
|
||||
|
||||
static void *producer_thread(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
int ctop_num = 0;
|
||||
int ptoc_num = 0;
|
||||
for (int i = 0; i < TOTAL_TEST_NUM; i++)
|
||||
{
|
||||
/* only two threads, use fake fix thread id */
|
||||
ctop_num += ringbuf_producer(0, rbf_ctop, BATCH_NUM);
|
||||
ptoc_num += ringbuf_customer(0, rbf_ptoc, BATCH_NUM);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *consumer_thread(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
int ctop_num = 0;
|
||||
int ptoc_num = 0;
|
||||
for (int i = 0; i < TOTAL_TEST_NUM; i++)
|
||||
{
|
||||
/* only two threads, use fake fix thread id */
|
||||
ptoc_num += ringbuf_producer(1, rbf_ptoc, BATCH_NUM);
|
||||
ctop_num += ringbuf_customer(1, rbf_ctop, BATCH_NUM);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int gtest_ringbuf(int mode)
|
||||
{
|
||||
pthread_t pid[2];
|
||||
test_ringbuf_push_mode = mode;
|
||||
rbf_ctop = stm_ringbuf_wrap_new(2, ringbuf_size);
|
||||
rbf_ptoc = stm_ringbuf_wrap_new(2, ringbuf_size);
|
||||
|
||||
pthread_create(&pid[0], NULL, producer_thread, NULL);
|
||||
pthread_create(&pid[1], NULL, consumer_thread, NULL);
|
||||
|
||||
pthread_join(pid[0], NULL);
|
||||
pthread_join(pid[1], NULL);
|
||||
|
||||
stm_ringbuf_get_statistics(rbf_ctop, NULL, &ctop_producer_len, NULL, &ctop_consumer_len);
|
||||
stm_ringbuf_get_statistics(rbf_ptoc, NULL, &ptoc_producer_len, NULL, &ptoc_consumer_len);
|
||||
|
||||
printf("ctop push len:%llu, ctop pop len:%llu\n", ctop_producer_len, ctop_consumer_len);
|
||||
printf("ptoc push len:%llu, ctop pop len:%llu\n", ptoc_producer_len, ptoc_consumer_len);
|
||||
if (ctop_producer_len != ctop_consumer_len)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (ptoc_producer_len != ptoc_consumer_len)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
stm_ringbuf_wrap_free(rbf_ptoc);
|
||||
stm_ringbuf_wrap_free(rbf_ctop);
|
||||
pthread_cancel(pid[0]);
|
||||
pthread_cancel(pid[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(MONITOR_RINGBUF, easy)
|
||||
{
|
||||
ASSERT_EQ(0, gtest_ringbuf(0));
|
||||
}
|
||||
|
||||
TEST(MONITOR_RINGBUF, stream)
|
||||
{
|
||||
ASSERT_EQ(0, gtest_ringbuf(0));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
68
test/monitor/gtest_rpc.cpp
Normal file
68
test/monitor/gtest_rpc.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <pthread.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "stellar/monitor.h"
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "monitor/monitor_rpc.h"
|
||||
|
||||
#define TEST_NUM 2000000
|
||||
static long g_worker_thread_run = 1;
|
||||
|
||||
static void *phony_worker_thread(void *arg)
|
||||
{
|
||||
struct monitor_rpc *rpc_ins = (struct monitor_rpc *)arg;
|
||||
while (g_worker_thread_run)
|
||||
{
|
||||
stm_rpc_exec(0, rpc_ins);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct iovec on_worker_thread_cb(int worker_thread_idx, struct iovec req, void *user_args)
|
||||
{
|
||||
EXPECT_EQ(0, worker_thread_idx);
|
||||
pthread_t *phony_worker_thread_id = (pthread_t *)user_args;
|
||||
EXPECT_EQ(*phony_worker_thread_id, pthread_self());
|
||||
|
||||
struct iovec resp = req;
|
||||
resp.iov_base = (void *)"world";
|
||||
resp.iov_len = 12345 + req.iov_len;
|
||||
return resp;
|
||||
}
|
||||
|
||||
TEST(MONITOR_RPC, base)
|
||||
{
|
||||
srand(time(NULL));
|
||||
pthread_t phony_worker_thread_id = 0;
|
||||
|
||||
struct monitor_rpc *rpc_ins = stm_rpc_new();
|
||||
ASSERT_TRUE(rpc_ins != NULL);
|
||||
|
||||
pthread_create(&phony_worker_thread_id, NULL, phony_worker_thread, rpc_ins);
|
||||
struct iovec req = {.iov_base = (void *)"hello", .iov_len = 5};
|
||||
usleep(10000);
|
||||
|
||||
for (int i = 0; i < TEST_NUM; i++)
|
||||
{
|
||||
req.iov_len = rand() % TEST_NUM;
|
||||
struct iovec resp = stm_rpc_call(rpc_ins, req, on_worker_thread_cb, &phony_worker_thread_id);
|
||||
EXPECT_EQ(req.iov_len + 12345, resp.iov_len);
|
||||
EXPECT_EQ(resp.iov_base, (void *)"world");
|
||||
}
|
||||
printf("rpc call num: %d\n", TEST_NUM);
|
||||
g_worker_thread_run = 0;
|
||||
pthread_cancel(phony_worker_thread_id);
|
||||
pthread_join(phony_worker_thread_id, NULL);
|
||||
stm_rpc_free(rpc_ins);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
152
test/monitor/gtest_sds.cpp
Normal file
152
test/monitor/gtest_sds.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "sds/sds.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(MONITOR_SDS, sds_new)
|
||||
{
|
||||
sds s = sdsnew("hello, world!");
|
||||
ASSERT_STREQ(s, "hello, world!");
|
||||
ASSERT_EQ(sdslen(s), strlen("hello, world!"));
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sds_cat)
|
||||
{
|
||||
sds s = sdsempty();
|
||||
ASSERT_EQ(sdslen(s), 0);
|
||||
|
||||
s = sdscat(s, "h");
|
||||
ASSERT_EQ(sdslen(s), 1);
|
||||
ASSERT_STREQ(s, "h");
|
||||
|
||||
s = sdscat(s, "ello");
|
||||
ASSERT_EQ(sdslen(s), 5);
|
||||
ASSERT_STREQ(s, "hello");
|
||||
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sds_cat_printf)
|
||||
{
|
||||
sds s = sdsempty();
|
||||
s = sdscatprintf(s, "%s", "hello");
|
||||
ASSERT_STREQ(s, "hello");
|
||||
|
||||
s = sdscatprintf(s, "%s", ",world!");
|
||||
ASSERT_STREQ(s, "hello,world!");
|
||||
|
||||
s = sdscatprintf(s, "%d", 200);
|
||||
ASSERT_STREQ(s, "hello,world!200");
|
||||
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sdstrim)
|
||||
{
|
||||
sds s = sdsnew("hello, world!\r\n");
|
||||
ASSERT_STRNE(s, "hello, world!");
|
||||
sds s1 = sdstrim(s, "\r\n");
|
||||
ASSERT_STREQ(s1, "hello, world!");
|
||||
sdsfree(s);
|
||||
|
||||
s = sdsnew("xxxhello, world!xxx\r\n");
|
||||
s1 = sdstrim(s, "xxx\r\n");
|
||||
ASSERT_STREQ(s1, "hello, world!");
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sdsrange)
|
||||
{
|
||||
sds s = sdsnew("1234567890");
|
||||
ASSERT_STREQ(s, "1234567890");
|
||||
ASSERT_EQ(10, sdslen(s));
|
||||
sdsrange(s, 0, 1);
|
||||
ASSERT_EQ(2, sdslen(s));
|
||||
ASSERT_STREQ(s, "12");
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sdscpylen)
|
||||
{
|
||||
sds s = sdsnew("1234567890");
|
||||
ASSERT_EQ(10, sdslen(s));
|
||||
sds s1 = sdscpylen(s, "abc", 3);
|
||||
ASSERT_STRNE(s1, "1234567890abc");
|
||||
sdsfree(s1);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sdssplitargs)
|
||||
{
|
||||
int argc = 0;
|
||||
sds *array = sdssplitargs("a b c d e f g", &argc);
|
||||
ASSERT_EQ(argc, 7);
|
||||
ASSERT_STREQ(array[0], "a");
|
||||
ASSERT_STREQ(array[1], "b");
|
||||
ASSERT_STREQ(array[2], "c");
|
||||
ASSERT_STREQ(array[3], "d");
|
||||
ASSERT_STREQ(array[4], "e");
|
||||
ASSERT_STREQ(array[5], "f");
|
||||
ASSERT_STREQ(array[6], "g");
|
||||
sdsfreesplitres(array, argc);
|
||||
|
||||
array = sdssplitargs("show", &argc);
|
||||
ASSERT_EQ(argc, 1);
|
||||
ASSERT_STREQ(array[0], "show");
|
||||
sdsfreesplitres(array, argc);
|
||||
|
||||
array = sdssplitargs("show ", &argc);
|
||||
ASSERT_EQ(argc, 1);
|
||||
ASSERT_STREQ(array[0], "show");
|
||||
sdsfreesplitres(array, argc);
|
||||
|
||||
array = sdssplitargs("", &argc);
|
||||
ASSERT_EQ(argc, 0);
|
||||
sdsfreesplitres(array, argc);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sdssplitlen)
|
||||
{
|
||||
int count = 0;
|
||||
const char *sep = "&";
|
||||
const char *request_url = "key1=value1&key2=value2&key3=value3";
|
||||
sds *array = sdssplitlen(request_url, strlen(request_url), sep, 1, &count);
|
||||
ASSERT_EQ(count, 3);
|
||||
ASSERT_STREQ(array[0], "key1=value1");
|
||||
ASSERT_STREQ(array[1], "key2=value2");
|
||||
ASSERT_STREQ(array[2], "key3=value3");
|
||||
|
||||
int key1_cnt;
|
||||
sds *key1_array = sdssplitlen(array[0], sdslen(array[0]), "=", 1, &key1_cnt);
|
||||
ASSERT_EQ(key1_cnt, 2);
|
||||
ASSERT_STREQ(key1_array[0], "key1");
|
||||
ASSERT_STREQ(key1_array[1], "value1");
|
||||
sdsfreesplitres(key1_array, key1_cnt);
|
||||
|
||||
sdsfreesplitres(array, count);
|
||||
}
|
||||
|
||||
TEST(MONITOR_SDS, sdsIncrLen)
|
||||
{
|
||||
sds s = sdsnew("hello, world!\r\n");
|
||||
ASSERT_EQ(sdslen(s), 15);
|
||||
sdsIncrLen(s, -2);
|
||||
ASSERT_EQ(sdslen(s), 13);
|
||||
EXPECT_STREQ(s, "hello, world!");
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
142
test/monitor/gtest_seek_layer.cpp
Normal file
142
test/monitor/gtest_seek_layer.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <pcap/pcap.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "stellar/packet.h"
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "stellar/packet.h"
|
||||
#include "packet_manager/packet_parser.h"
|
||||
#include "packet_manager/packet_internal.h"
|
||||
|
||||
/* mock */
|
||||
int packet_manager_subscribe(UNUSED struct packet_manager *pkt_mgr, UNUSED enum packet_stage stage, UNUSED on_packet_stage_callback *cb, UNUSED void *args)
|
||||
{
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_layer(const struct packet *pkt, const enum layer_proto *layers, int expect_layer_cnt)
|
||||
{
|
||||
int actual_pkt_layer = packet_get_layer_count(pkt);
|
||||
if (actual_pkt_layer != expect_layer_cnt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < expect_layer_cnt; i++)
|
||||
{
|
||||
const struct layer *layer = packet_get_layer_by_idx(pkt, i);
|
||||
if (layer == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (layer->proto != layers[i])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(MONITOR_SEEK_LAYER, ethernet_ipv4_udp_dns)
|
||||
{
|
||||
struct packet pkt = {};
|
||||
/* a DNS query packet */
|
||||
const unsigned char packet_bytes[] =
|
||||
{
|
||||
0xe8, 0x1c, 0xba, 0xcc, 0x87, 0x21, 0x7c, 0x2a,
|
||||
0x31, 0x9f, 0x98, 0x2c, 0x08, 0x00, 0x45, 0x00,
|
||||
0x00, 0x3e, 0x4a, 0x41, 0x00, 0x00, 0x80, 0x11,
|
||||
0x26, 0x7a, 0xc0, 0xa8, 0x24, 0x67, 0x72, 0x72,
|
||||
0x72, 0x72, 0xd5, 0xbc, 0x00, 0x35, 0x00, 0x2a,
|
||||
0xd7, 0x33, 0xdf, 0x96, 0x01, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x66,
|
||||
0x75, 0x77, 0x75, 0x04, 0x62, 0x64, 0x70, 0x66,
|
||||
0x03, 0x6f, 0x72, 0x67, 0x02, 0x63, 0x6e, 0x00,
|
||||
0x00, 0x01, 0x00, 0x01};
|
||||
|
||||
ASSERT_TRUE(packet_parse(&pkt, (char *)packet_bytes, sizeof(packet_bytes)) != NULL);
|
||||
int pkt_layer = packet_get_layer_count(&pkt);
|
||||
ASSERT_EQ(pkt_layer, 3);
|
||||
enum layer_proto layers[3] = {LAYER_PROTO_ETHER, LAYER_PROTO_IPV4, LAYER_PROTO_UDP};
|
||||
ASSERT_EQ(0, check_layer(&pkt, layers, 3));
|
||||
}
|
||||
|
||||
TEST(MONITOR_SEEK_LAYER, ethernet_ipv4_gre_ipv4_gre_ipv4_udp_dns)
|
||||
{
|
||||
struct packet pkt = {};
|
||||
/* a DNS query packet */
|
||||
const unsigned char packet_bytes[] = {
|
||||
0x02, 0x00, 0x00, 0x01, 0x02, 0x95, 0x00, 0x00,
|
||||
0xc3, 0x51, 0x05, 0x0f, 0x08, 0x00, 0x45, 0x00,
|
||||
0x00, 0x7c, 0x46, 0xdb, 0x00, 0x00, 0xfb, 0x2f,
|
||||
0xcc, 0x35, 0x73, 0x99, 0x4b, 0xf6, 0x7a, 0x70,
|
||||
0x72, 0x42, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||
0x03, 0x84, 0x45, 0x00, 0x00, 0x60, 0xc8, 0xd5,
|
||||
0x00, 0x00, 0xff, 0x2f, 0xe9, 0x58, 0x0a, 0x96,
|
||||
0xfa, 0x0a, 0x0a, 0x96, 0xfa, 0x09, 0x00, 0x00,
|
||||
0x08, 0x00, 0x45, 0x00, 0x00, 0x48, 0xb2, 0xc6,
|
||||
0x00, 0x00, 0x3f, 0x11, 0x6a, 0x4b, 0x7a, 0x70,
|
||||
0x72, 0x49, 0x73, 0xee, 0xfd, 0xeb, 0x51, 0x13,
|
||||
0x00, 0x35, 0x00, 0x34, 0x3a, 0xbf, 0xb1, 0xba,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x08, 0x75, 0x72, 0x77, 0x70, 0x75,
|
||||
0x6c, 0x65, 0x70, 0x03, 0x77, 0x77, 0x77, 0x09,
|
||||
0x64, 0x65, 0x73, 0x68, 0x65, 0x6e, 0x67, 0x32,
|
||||
0x38, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
|
||||
0x00, 0x01};
|
||||
|
||||
ASSERT_TRUE(packet_parse(&pkt, (char *)packet_bytes, sizeof(packet_bytes)) != NULL);
|
||||
int pkt_layer = packet_get_layer_count(&pkt);
|
||||
ASSERT_EQ(pkt_layer, 7);
|
||||
enum layer_proto layers[7] = {LAYER_PROTO_ETHER, LAYER_PROTO_IPV4, LAYER_PROTO_GRE, LAYER_PROTO_IPV4, LAYER_PROTO_GRE, LAYER_PROTO_IPV4, LAYER_PROTO_UDP};
|
||||
ASSERT_EQ(0, check_layer(&pkt, layers, 7));
|
||||
}
|
||||
|
||||
extern "C" u_int stm_bpf_filter_greedy(const struct bpf_insn *bpf_dlt_raw, struct packet *pkt);
|
||||
TEST(MONITOR_SEEK_LAYER, ethernet_ipv4_gre_ipv4_gre_ipv4_udp_dns_bpf_filter_greedy)
|
||||
{
|
||||
struct packet pkt = {};
|
||||
/* a DNS query packet */
|
||||
const unsigned char packet_bytes[] = {
|
||||
0x02, 0x00, 0x00, 0x01, 0x02, 0x95, 0x00, 0x00,
|
||||
0xc3, 0x51, 0x05, 0x0f, 0x08, 0x00, 0x45, 0x00,
|
||||
0x00, 0x7c, 0x46, 0xdb, 0x00, 0x00, 0xfb, 0x2f,
|
||||
0xcc, 0x35, 0x73, 0x99, 0x4b, 0xf6, 0x7a, 0x70,
|
||||
0x72, 0x42, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||
0x03, 0x84, 0x45, 0x00, 0x00, 0x60, 0xc8, 0xd5,
|
||||
0x00, 0x00, 0xff, 0x2f, 0xe9, 0x58, 0x0a, 0x96,
|
||||
0xfa, 0x0a, 0x0a, 0x96, 0xfa, 0x09, 0x00, 0x00,
|
||||
0x08, 0x00, 0x45, 0x00, 0x00, 0x48, 0xb2, 0xc6,
|
||||
0x00, 0x00, 0x3f, 0x11, 0x6a, 0x4b, 0x7a, 0x70,
|
||||
0x72, 0x49, 0x73, 0xee, 0xfd, 0xeb, 0x51, 0x13,
|
||||
0x00, 0x35, 0x00, 0x34, 0x3a, 0xbf, 0xb1, 0xba,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x08, 0x75, 0x72, 0x77, 0x70, 0x75,
|
||||
0x6c, 0x65, 0x70, 0x03, 0x77, 0x77, 0x77, 0x09,
|
||||
0x64, 0x65, 0x73, 0x68, 0x65, 0x6e, 0x67, 0x32,
|
||||
0x38, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
|
||||
0x00, 0x01};
|
||||
|
||||
ASSERT_TRUE(packet_parse(&pkt, (char *)packet_bytes, sizeof(packet_bytes)) != NULL);
|
||||
|
||||
struct bpf_program bpf_bin;
|
||||
pcap_compile_nopcap(65535, DLT_RAW, &bpf_bin, "host 115.153.75.246 and host 122.112.114.66", 1, 0); // firset ipv4 layer
|
||||
ASSERT_TRUE(0 != stm_bpf_filter_greedy(bpf_bin.bf_insns, &pkt));
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(65535, DLT_RAW, &bpf_bin, "host 10.150.250.10 and host 10.150.250.9", 1, 0); // second ipv4 layer
|
||||
ASSERT_TRUE(0 != stm_bpf_filter_greedy(bpf_bin.bf_insns, &pkt));
|
||||
pcap_freecode(&bpf_bin);
|
||||
|
||||
pcap_compile_nopcap(65535, DLT_RAW, &bpf_bin, "host 122.112.114.73 and host 115.238.253.235", 1, 0); // third ipv4 layer
|
||||
ASSERT_TRUE(0 != stm_bpf_filter_greedy(bpf_bin.bf_insns, &pkt));
|
||||
pcap_freecode(&bpf_bin);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
49
test/monitor/gtest_spinlock.cpp
Normal file
49
test/monitor/gtest_spinlock.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "monitor/monitor_private.h"
|
||||
|
||||
static volatile long sum = 0;
|
||||
static volatile long barrier = 1;
|
||||
#define CALC_NUM 10000000
|
||||
static void *calc_thread(void *arg)
|
||||
{
|
||||
stm_spinlock *lock = (stm_spinlock *)arg;
|
||||
(void)lock;
|
||||
while (barrier)
|
||||
;
|
||||
for (int i = 0; i < CALC_NUM; i++)
|
||||
{
|
||||
stm_spinlock_lock(lock);
|
||||
sum++;
|
||||
stm_spinlock_unlock(lock);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST(MONITOR_SPINLOCK, base)
|
||||
{
|
||||
pthread_t pid;
|
||||
stm_spinlock *lock = stm_spinlock_new();
|
||||
pthread_create(&pid, NULL, calc_thread, (void *)lock);
|
||||
usleep(5000);
|
||||
barrier = 0;
|
||||
for (int i = 0; i < CALC_NUM; i++)
|
||||
{
|
||||
stm_spinlock_lock(lock);
|
||||
sum++;
|
||||
stm_spinlock_unlock(lock);
|
||||
}
|
||||
pthread_join(pid, NULL);
|
||||
stm_spinlock_free(lock);
|
||||
EXPECT_EQ(sum, CALC_NUM * 2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
535
test/monitor/gtest_topk.cpp
Normal file
535
test/monitor/gtest_topk.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
#include <assert.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/time.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <arpa/inet.h>
|
||||
// #include <fieldstat.h>
|
||||
#include <fieldstat/fieldstat_exporter.h>
|
||||
|
||||
#define MAX_TOPK_CELL_NUM 100
|
||||
#define MAX_TOPK_METRIC_NUM 255
|
||||
#define SHOW_TOPK_RESULT_NUM 10
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define TOPK_DIMENSION_NUM 8
|
||||
static const char *g_topk_dimension[TOPK_DIMENSION_NUM] = {
|
||||
"top_client_ip",
|
||||
"top_server_ip",
|
||||
"top_internal_ip",
|
||||
"top_external_ip",
|
||||
"top_server_fqdn",
|
||||
"top_server_domain",
|
||||
"top_client_country",
|
||||
"top_server_country",
|
||||
};
|
||||
|
||||
#define TOPK_RANK_BY_NUM 3
|
||||
static const char *g_topk_rank_by[TOPK_RANK_BY_NUM] = {
|
||||
"sessions",
|
||||
"packets",
|
||||
"bytes",
|
||||
};
|
||||
|
||||
#define TOPK_METRIC_NUM 3
|
||||
/* index corresponding to g_topk_rank_by[] */
|
||||
static const char *g_metric_name[TOPK_METRIC_NUM] = {
|
||||
"sessions",
|
||||
"pkts",
|
||||
"bytes",
|
||||
};
|
||||
struct topk_t
|
||||
{
|
||||
char name[128];
|
||||
long long value;
|
||||
};
|
||||
|
||||
static int g_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
|
||||
static int g_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
|
||||
|
||||
static struct fieldstat *g_fs4_ins = NULL;
|
||||
|
||||
/* the biggest value index is 0 */
|
||||
static int qsort_topk_value_cmp_cb(const void *a, const void *b)
|
||||
{
|
||||
struct topk_t *la = (struct topk_t *)a;
|
||||
struct topk_t *lb = (struct topk_t *)b;
|
||||
return (int)(lb->value - la->value);
|
||||
}
|
||||
|
||||
// lijia temp add for test
|
||||
|
||||
static void show_topk_stat(const char *dimension, const char *rank_by, struct topk_t *topk_result, size_t n_result, size_t topk)
|
||||
{
|
||||
|
||||
if (n_result)
|
||||
{
|
||||
printf("### top '%s' rank_by '%s' result %zu: \n", dimension, rank_by, topk);
|
||||
qsort((void *)topk_result, n_result, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
|
||||
int show_num = MIN(topk, n_result);
|
||||
for (int i = 0; i < show_num; i++)
|
||||
{
|
||||
printf("%2d) %16s\t%8lld\n", i + 1, topk_result[i].name, topk_result[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_topk_cube_id(struct fieldstat *fs4_ins, const char *dimension, const char *rank_by)
|
||||
{
|
||||
struct field find_cube_tmp[2] = {};
|
||||
find_cube_tmp[0].key = "name";
|
||||
find_cube_tmp[0].type = FIELD_VALUE_CSTRING;
|
||||
find_cube_tmp[0].value_str = dimension;
|
||||
find_cube_tmp[1].key = "rank_by";
|
||||
find_cube_tmp[1].type = FIELD_VALUE_CSTRING;
|
||||
find_cube_tmp[1].value_str = rank_by;
|
||||
return fieldstat_find_cube(fs4_ins, find_cube_tmp, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
dimensions: client_ip, server_ip, internal_ip, external_ip, server_fqdn, server_domain, client_country, server_country
|
||||
rank_by: sessions, bytes, packets
|
||||
metric: sessions, bytes, packets
|
||||
*/
|
||||
static int get_topk_rank_by(struct fieldstat *fs4_ins, const char *dimension, const char *rank_by, const char *metric_name, struct topk_t *topk_array, size_t max_array_num)
|
||||
{
|
||||
int cube_id = get_topk_cube_id(fs4_ins, dimension, rank_by);
|
||||
assert(cube_id >= 0);
|
||||
|
||||
int metric_id = fieldstat_cube_get_metric_id_by_name(fs4_ins, cube_id, metric_name);
|
||||
assert(metric_id >= 0);
|
||||
|
||||
struct field_list *cell_dimensions_list = NULL;
|
||||
size_t n_cell = 0;
|
||||
fieldstat_cube_get_cells(fs4_ins, cube_id, &cell_dimensions_list, &n_cell);
|
||||
if (NULL == cell_dimensions_list)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
size_t result_min = MIN(max_array_num, n_cell);
|
||||
for (size_t ce = 0; ce < result_min; ce++)
|
||||
{
|
||||
strncpy(topk_array[ce].name, cell_dimensions_list[ce].field->value_str, sizeof(topk_array[ce].name));
|
||||
fieldstat_counter_get(fs4_ins, cube_id, &cell_dimensions_list[ce], metric_id, &topk_array[ce].value);
|
||||
}
|
||||
fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
|
||||
return result_min;
|
||||
}
|
||||
|
||||
static void stm_topk_register_cube(struct fieldstat *fs4_ins, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM])
|
||||
{
|
||||
struct field reg_cube_tmp[2] = {};
|
||||
for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
|
||||
{
|
||||
for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
|
||||
{
|
||||
reg_cube_tmp[0].key = "name";
|
||||
reg_cube_tmp[0].type = FIELD_VALUE_CSTRING;
|
||||
reg_cube_tmp[0].value_str = g_topk_dimension[i];
|
||||
reg_cube_tmp[1].key = "rank_by";
|
||||
reg_cube_tmp[1].type = FIELD_VALUE_CSTRING;
|
||||
reg_cube_tmp[1].value_str = g_topk_rank_by[j];
|
||||
cube_id[i][j] = fieldstat_cube_create(fs4_ins, reg_cube_tmp, 2);
|
||||
assert(cube_id[i][j] >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stm_topk_register_counter(struct fieldstat *fs4_ins, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM],
|
||||
int counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM])
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
|
||||
{
|
||||
for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
|
||||
{
|
||||
for (size_t k = 0; k < sizeof(g_metric_name) / sizeof(char *); k++)
|
||||
{
|
||||
counter_id[i][j][k] = fieldstat_register_counter(fs4_ins, cube_id[i][j], g_metric_name[k]);
|
||||
assert(counter_id[i][j][k] >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stm_topk_set_cube_sampling(struct fieldstat *fs4_ins, enum sampling_mode mode, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM],
|
||||
int counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM])
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
|
||||
{
|
||||
for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
|
||||
{
|
||||
fieldstat_cube_set_sampling(fs4_ins, cube_id[i][j], mode, MAX_TOPK_CELL_NUM, counter_id[i][j][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int stm_topk_client_ip_rankby_sessions(void)
|
||||
{
|
||||
struct field ip_tag = {};
|
||||
ip_tag.key = "top_client_ip";
|
||||
ip_tag.type = FIELD_VALUE_CSTRING;
|
||||
unsigned int begin_ip_addr = 0x04030201;
|
||||
struct topk_t benchmark_result[MAX_TOPK_METRIC_NUM];
|
||||
long long max_value = 0;
|
||||
memset(benchmark_result, 0, sizeof(benchmark_result));
|
||||
for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
|
||||
{
|
||||
benchmark_result[i].value = rand() % 10000;
|
||||
if (benchmark_result[i].value > max_value)
|
||||
{
|
||||
max_value = benchmark_result[i].value;
|
||||
}
|
||||
unsigned int ip_addr_net = htonl(begin_ip_addr + i);
|
||||
inet_ntop(AF_INET, &ip_addr_net, benchmark_result[i].name, sizeof(benchmark_result[i].name));
|
||||
ip_tag.value_str = benchmark_result[i].name;
|
||||
fieldstat_counter_incrby(g_fs4_ins, g_topk_cubeid[0][0], g_topk_counter_id[0][0][0], &ip_tag, 1, benchmark_result[i].value);
|
||||
}
|
||||
|
||||
/*** query and verify ***/
|
||||
struct topk_t fs4_topk_result[MAX_TOPK_CELL_NUM] = {};
|
||||
int num = get_topk_rank_by(g_fs4_ins, "top_client_ip", "sessions", "sessions", fs4_topk_result, sizeof(fs4_topk_result) / sizeof(struct topk_t));
|
||||
assert(num > 0);
|
||||
printf("topk top_client_ip rankby sessions, max value:%lld\n", max_value);
|
||||
show_topk_stat("top_client_ip", "sessions", fs4_topk_result, num, SHOW_TOPK_RESULT_NUM);
|
||||
qsort((void *)benchmark_result, MAX_TOPK_METRIC_NUM, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
|
||||
assert(max_value == fs4_topk_result[0].value);
|
||||
return memcmp(fs4_topk_result, &benchmark_result[0], SHOW_TOPK_RESULT_NUM);
|
||||
}
|
||||
|
||||
static int stm_topk_server_ip_rankby_packets(void)
|
||||
{
|
||||
struct field ip_tag = {};
|
||||
ip_tag.key = "top_server_ip";
|
||||
ip_tag.type = FIELD_VALUE_CSTRING;
|
||||
unsigned int begin_ip_addr = 0x64640101;
|
||||
long long max_value = 0;
|
||||
struct topk_t benchmark_result[MAX_TOPK_METRIC_NUM];
|
||||
memset(benchmark_result, 0, sizeof(benchmark_result));
|
||||
for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
|
||||
{
|
||||
benchmark_result[i].value = rand() % 10000;
|
||||
if (benchmark_result[i].value > max_value)
|
||||
{
|
||||
max_value = benchmark_result[i].value;
|
||||
}
|
||||
unsigned int ip_addr_net = htonl(begin_ip_addr + i);
|
||||
inet_ntop(AF_INET, &ip_addr_net, benchmark_result[i].name, sizeof(benchmark_result[i].name));
|
||||
ip_tag.value_str = benchmark_result[i].name;
|
||||
fieldstat_counter_incrby(g_fs4_ins, g_topk_cubeid[1][1], g_topk_counter_id[1][1][1], &ip_tag, 1, benchmark_result[i].value);
|
||||
}
|
||||
|
||||
/*** query and verify ***/
|
||||
struct topk_t fs4_topk_result[MAX_TOPK_CELL_NUM] = {};
|
||||
int num = get_topk_rank_by(g_fs4_ins, "top_server_ip", "packets", "pkts", fs4_topk_result, sizeof(fs4_topk_result) / sizeof(struct topk_t));
|
||||
assert(num > 0);
|
||||
printf("topk top_server_ip rankby packets, max value:%lld\n", max_value);
|
||||
show_topk_stat("top_server_ip", "packets", fs4_topk_result, num, SHOW_TOPK_RESULT_NUM);
|
||||
qsort((void *)benchmark_result, MAX_TOPK_METRIC_NUM, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
|
||||
assert(max_value == fs4_topk_result[0].value);
|
||||
return memcmp(fs4_topk_result, &benchmark_result[0], SHOW_TOPK_RESULT_NUM);
|
||||
}
|
||||
|
||||
static int stm_topk_server_domain_rankby_bytes(void)
|
||||
{
|
||||
struct field domain_tag = {};
|
||||
domain_tag.key = "top_server_domain";
|
||||
domain_tag.type = FIELD_VALUE_CSTRING;
|
||||
struct topk_t benchmark_result[MAX_TOPK_METRIC_NUM];
|
||||
memset(benchmark_result, 0, sizeof(benchmark_result));
|
||||
for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
|
||||
{
|
||||
snprintf(benchmark_result[i].name, sizeof(benchmark_result[i].name), "www.abcd.com");
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_TOPK_METRIC_NUM; i++)
|
||||
{
|
||||
benchmark_result[i].value = rand() % 10000;
|
||||
benchmark_result[i].name[4] = rand() % 26 + 'a';
|
||||
benchmark_result[i].name[5] = rand() % 26 + 'a';
|
||||
benchmark_result[i].name[6] = rand() % 26 + 'a';
|
||||
benchmark_result[i].name[7] = rand() % 26 + 'a';
|
||||
domain_tag.value_str = benchmark_result[i].name;
|
||||
fieldstat_counter_incrby(g_fs4_ins, g_topk_cubeid[5][2], g_topk_counter_id[5][2][2], &domain_tag, 1, benchmark_result[i].value);
|
||||
}
|
||||
|
||||
/*** query and verify ***/
|
||||
struct topk_t fs4_topk_result[SHOW_TOPK_RESULT_NUM] = {};
|
||||
int num = get_topk_rank_by(g_fs4_ins, "top_server_domain", "bytes", "bytes", fs4_topk_result, sizeof(fs4_topk_result) / sizeof(struct topk_t));
|
||||
assert(num > 0);
|
||||
show_topk_stat("top_server_domain", "bytes", fs4_topk_result, num, SHOW_TOPK_RESULT_NUM);
|
||||
qsort((void *)benchmark_result, MAX_TOPK_METRIC_NUM, sizeof(struct topk_t), qsort_topk_value_cmp_cb);
|
||||
return memcmp(fs4_topk_result, &benchmark_result[0], SHOW_TOPK_RESULT_NUM);
|
||||
}
|
||||
|
||||
class MONITOR_TOPK : public testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
srand(time(NULL));
|
||||
g_fs4_ins = fieldstat_new();
|
||||
assert(g_fs4_ins != NULL);
|
||||
|
||||
stm_topk_register_cube(g_fs4_ins, g_topk_cubeid);
|
||||
stm_topk_register_counter(g_fs4_ins, g_topk_cubeid, g_topk_counter_id);
|
||||
stm_topk_set_cube_sampling(g_fs4_ins, SAMPLING_MODE_TOPK, g_topk_cubeid, g_topk_counter_id);
|
||||
}
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
fieldstat_reset(g_fs4_ins);
|
||||
fieldstat_free(g_fs4_ins);
|
||||
}
|
||||
};
|
||||
|
||||
static void generate_random_counter(struct fieldstat *fs4_ins, int cube_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM],
|
||||
int counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM])
|
||||
{
|
||||
struct field tag = {};
|
||||
tag.type = FIELD_VALUE_CSTRING;
|
||||
tag.value_str = "random";
|
||||
|
||||
for (size_t i = 0; i < sizeof(g_topk_dimension) / sizeof(g_topk_dimension[0]); i++)
|
||||
{
|
||||
for (size_t j = 0; j < sizeof(g_topk_rank_by) / sizeof(g_topk_rank_by[0]); j++)
|
||||
{
|
||||
for (size_t k = 0; k < sizeof(g_metric_name) / sizeof(char *); k++)
|
||||
{
|
||||
assert(g_topk_counter_id[i][j][k] >= 0);
|
||||
tag.key = g_topk_dimension[i];
|
||||
fieldstat_counter_incrby(fs4_ins, cube_id[i][j], counter_id[i][j][k], &tag, 1, random());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char **show_topk_get_metrics_name(struct fieldstat *show_fs4_ins, int *metric_array_num)
|
||||
{
|
||||
int *cube_id_array = NULL;
|
||||
int n_cube = 0;
|
||||
fieldstat_get_cubes(show_fs4_ins, &cube_id_array, &n_cube);
|
||||
if (NULL == cube_id_array)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct field_list *cell_dimensions_list = NULL;
|
||||
size_t n_cell = 0;
|
||||
fieldstat_cube_get_cells(show_fs4_ins, cube_id_array[0], &cell_dimensions_list, &n_cell);
|
||||
if (cell_dimensions_list == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get metric name of any valid cube_id */
|
||||
int *metric_id_out;
|
||||
size_t n_metric;
|
||||
char **metric_array = NULL;
|
||||
fieldstat_cell_get_metrics(show_fs4_ins, cube_id_array[0], cell_dimensions_list, &metric_id_out, &n_metric);
|
||||
if (n_metric == 0)
|
||||
{
|
||||
fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
|
||||
return NULL;
|
||||
}
|
||||
metric_array = (char **)malloc(sizeof(char *) * n_metric);
|
||||
for (size_t j = 0; j < n_metric; j++)
|
||||
{
|
||||
metric_array[j] = strdup(fieldstat_metric_get_name(show_fs4_ins, cube_id_array[0], metric_id_out[j]));
|
||||
}
|
||||
fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
|
||||
|
||||
*metric_array_num = n_metric;
|
||||
return metric_array;
|
||||
}
|
||||
|
||||
TEST(MONITOR, show_all_valid_metrics)
|
||||
{
|
||||
struct fieldstat *show_fs4_ins = fieldstat_new();
|
||||
int show_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
|
||||
int show_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
|
||||
|
||||
stm_topk_register_cube(show_fs4_ins, show_topk_cubeid);
|
||||
stm_topk_register_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
|
||||
stm_topk_set_cube_sampling(show_fs4_ins, SAMPLING_MODE_TOPK, show_topk_cubeid, show_topk_counter_id);
|
||||
|
||||
generate_random_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
|
||||
int metric_array_num = 0;
|
||||
char **metric_array = show_topk_get_metrics_name(show_fs4_ins, &metric_array_num);
|
||||
EXPECT_TRUE(metric_array_num > 0);
|
||||
EXPECT_TRUE(metric_array != NULL);
|
||||
|
||||
for (int i = 0; i < metric_array_num; i++)
|
||||
{
|
||||
printf("metric name: %s\n", metric_array[i]);
|
||||
free(metric_array[i]);
|
||||
}
|
||||
free(metric_array);
|
||||
|
||||
fieldstat_free(show_fs4_ins);
|
||||
}
|
||||
|
||||
static int charchar_array_exist(char **dimension_array, int dimension_array_num, const char *expect_name)
|
||||
{
|
||||
for (int i = 0; i < dimension_array_num && dimension_array[i] != NULL; i++)
|
||||
{
|
||||
if (0 == strcmp(dimension_array[i], expect_name))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **show_topk_get_dimensions_name(struct fieldstat *show_fs4_ins, int *dimension_array_result)
|
||||
{
|
||||
int *cube_id_array = NULL;
|
||||
int n_cube = 0;
|
||||
fieldstat_get_cubes(show_fs4_ins, &cube_id_array, &n_cube);
|
||||
if (0 == n_cube)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
char **dimension_array = NULL;
|
||||
int dimension_array_num = 0;
|
||||
|
||||
struct field_list *cell_dimensions_list = NULL;
|
||||
for (int i = 0; i < n_cube; i++)
|
||||
{
|
||||
size_t n_cell = 0;
|
||||
fieldstat_cube_get_cells(show_fs4_ins, cube_id_array[i], &cell_dimensions_list, &n_cell);
|
||||
if (NULL == cell_dimensions_list)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (size_t j = 0; j < cell_dimensions_list->n_field; j++)
|
||||
{
|
||||
if (0 == charchar_array_exist(dimension_array, dimension_array_num, cell_dimensions_list->field[j].key))
|
||||
{
|
||||
dimension_array = (char **)realloc(dimension_array, sizeof(char *) * (dimension_array_num + 1));
|
||||
dimension_array[dimension_array_num] = strdup(cell_dimensions_list->field[j].key);
|
||||
dimension_array_num++;
|
||||
}
|
||||
}
|
||||
fieldstat_field_list_arr_free(cell_dimensions_list, n_cell);
|
||||
}
|
||||
|
||||
*dimension_array_result = dimension_array_num;
|
||||
return dimension_array;
|
||||
}
|
||||
|
||||
TEST(MONITOR, show_all_valid_dimensions)
|
||||
{
|
||||
struct fieldstat *show_fs4_ins = fieldstat_new();
|
||||
int show_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
|
||||
int show_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
|
||||
|
||||
stm_topk_register_cube(show_fs4_ins, show_topk_cubeid);
|
||||
stm_topk_register_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
|
||||
stm_topk_set_cube_sampling(show_fs4_ins, SAMPLING_MODE_TOPK, show_topk_cubeid, show_topk_counter_id);
|
||||
|
||||
generate_random_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
|
||||
|
||||
int dimension_array_num = 0;
|
||||
char **dimension_array = show_topk_get_dimensions_name(show_fs4_ins, &dimension_array_num);
|
||||
EXPECT_TRUE(dimension_array_num > 0);
|
||||
EXPECT_TRUE(dimension_array != NULL);
|
||||
|
||||
for (int i = 0; i < dimension_array_num; i++)
|
||||
{
|
||||
printf("dimension name: %s\n", dimension_array[i]);
|
||||
free(dimension_array[i]);
|
||||
}
|
||||
free(dimension_array);
|
||||
fieldstat_free(show_fs4_ins);
|
||||
}
|
||||
|
||||
char **show_topk_get_rankby_name(struct fieldstat *show_fs4_ins, int *rankby_array_result)
|
||||
{
|
||||
int *cube_id_array = NULL;
|
||||
int n_cube = 0;
|
||||
fieldstat_get_cubes(show_fs4_ins, &cube_id_array, &n_cube);
|
||||
if (NULL == cube_id_array)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
char **rankby_array = NULL;
|
||||
int rankby_array_num = 0;
|
||||
|
||||
for (int i = 0; i < n_cube; i++)
|
||||
{
|
||||
struct field_list *flist = fieldstat_cube_get_dimension(show_fs4_ins, cube_id_array[i]);
|
||||
if (flist == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (size_t j = 0; j < flist->n_field; j++)
|
||||
{
|
||||
if (0 == strcmp(flist->field[j].key, "rank_by"))
|
||||
{
|
||||
if (0 == charchar_array_exist(rankby_array, rankby_array_num, flist->field[j].value_str))
|
||||
{
|
||||
rankby_array = (char **)realloc(rankby_array, sizeof(char *) * (rankby_array_num + 1));
|
||||
rankby_array[rankby_array_num] = strdup(flist->field[j].value_str);
|
||||
rankby_array_num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldstat_field_list_arr_free(flist, 1);
|
||||
}
|
||||
*rankby_array_result = rankby_array_num;
|
||||
return rankby_array;
|
||||
}
|
||||
|
||||
TEST(MONITOR, show_all_valid_rankby)
|
||||
{
|
||||
struct fieldstat *show_fs4_ins = fieldstat_new();
|
||||
int show_topk_cubeid[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM] = {}; // [dimension][rank_by]
|
||||
int show_topk_counter_id[TOPK_DIMENSION_NUM][TOPK_RANK_BY_NUM][TOPK_METRIC_NUM] = {}; // [dimension][rank_by][metric]
|
||||
|
||||
stm_topk_register_cube(show_fs4_ins, show_topk_cubeid);
|
||||
stm_topk_register_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
|
||||
stm_topk_set_cube_sampling(show_fs4_ins, SAMPLING_MODE_TOPK, show_topk_cubeid, show_topk_counter_id);
|
||||
|
||||
generate_random_counter(show_fs4_ins, show_topk_cubeid, show_topk_counter_id);
|
||||
|
||||
int rankby_array_num = 0;
|
||||
char **rankby_array = show_topk_get_rankby_name(show_fs4_ins, &rankby_array_num);
|
||||
EXPECT_TRUE(rankby_array_num > 0);
|
||||
EXPECT_TRUE(rankby_array != NULL);
|
||||
|
||||
for (int i = 0; i < rankby_array_num; i++)
|
||||
{
|
||||
printf("rankby name: %s\n", rankby_array[i]);
|
||||
free(rankby_array[i]);
|
||||
}
|
||||
free(rankby_array);
|
||||
|
||||
fieldstat_free(show_fs4_ins);
|
||||
}
|
||||
|
||||
TEST_F(MONITOR_TOPK, show_client_ip_by_sessions)
|
||||
{
|
||||
EXPECT_EQ(0, stm_topk_client_ip_rankby_sessions());
|
||||
}
|
||||
|
||||
TEST_F(MONITOR_TOPK, show_server_ip_by_packets)
|
||||
{
|
||||
EXPECT_EQ(0, stm_topk_server_ip_rankby_packets());
|
||||
}
|
||||
|
||||
TEST_F(MONITOR_TOPK, show_server_domain_by_bytes)
|
||||
{
|
||||
EXPECT_EQ(0, stm_topk_server_domain_rankby_bytes());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
74
test/monitor/monitor_main.cpp
Normal file
74
test/monitor/monitor_main.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "monitor/monitor_private.h"
|
||||
#include "stellar/packet.h"
|
||||
#include "stellar/stellar.h"
|
||||
|
||||
/* main function to drives the stellar library to run,
|
||||
using a delay plugin to increase the running time
|
||||
*/
|
||||
|
||||
static int sleep_per_packet_ms = 0;
|
||||
static int sleep_before_run_ms = 0;
|
||||
|
||||
static void delay_packet_cb(struct packet *pkt, enum packet_stage stage, void *plugin_env)
|
||||
{
|
||||
(void)stage;
|
||||
(void)pkt;
|
||||
(void)plugin_env;
|
||||
if (sleep_per_packet_ms)
|
||||
{
|
||||
fprintf(stderr, "packet dump, sleep %d ms....\n", sleep_per_packet_ms);
|
||||
usleep(1000 * sleep_per_packet_ms);
|
||||
}
|
||||
}
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [sleep_before_run_ms] [sleep_per_packet_ms]\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
sleep_before_run_ms = atoi(argv[1]);
|
||||
if (argc >= 3)
|
||||
{
|
||||
sleep_per_packet_ms = atoi(argv[2]);
|
||||
}
|
||||
}
|
||||
struct stellar *st = stellar_new("./conf/stellar.toml");
|
||||
assert(st);
|
||||
if (NULL == st)
|
||||
{
|
||||
fprintf(stderr, "stellar_new error!\n");
|
||||
exit(1);
|
||||
}
|
||||
struct stellar_monitor *stm = stellar_monitor_get();
|
||||
struct module *packet_mgr_mod = module_manager_get_module(stm->mod_mgr_ref, PACKET_MANAGER_MODULE_NAME);
|
||||
struct packet_manager *pkt_mgr = module_to_packet_manager(packet_mgr_mod);
|
||||
int ret = packet_manager_subscribe(pkt_mgr, PACKET_STAGE_INPUT, delay_packet_cb, stm);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "packet_manager_subscribe error!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sleep_before_run_ms)
|
||||
{
|
||||
fprintf(stderr, "%s, sleep %d ms before run....\n", argv[0], sleep_before_run_ms);
|
||||
usleep(1000 * sleep_before_run_ms);
|
||||
}
|
||||
fprintf(stderr, "%s starting...\r\n", argv[0]);
|
||||
stellar_run(st);
|
||||
stellar_free(st);
|
||||
return 0;
|
||||
}
|
||||
BIN
test/monitor/pcap/monitor_benchmark.pcap
Normal file
BIN
test/monitor/pcap/monitor_benchmark.pcap
Normal file
Binary file not shown.
BIN
test/monitor/pcap/monitor_packet_dump.pcap
Normal file
BIN
test/monitor/pcap/monitor_packet_dump.pcap
Normal file
Binary file not shown.
BIN
test/monitor/pcap/monitor_tunnel.pcap
Normal file
BIN
test/monitor/pcap/monitor_tunnel.pcap
Normal file
Binary file not shown.
1
tools/CMakeLists.txt
Normal file
1
tools/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(monitor)
|
||||
19
tools/monitor/CMakeLists.txt
Normal file
19
tools/monitor/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
#like redis-cli, use hyphen('-') not underscore('_')
|
||||
add_executable(stellar-cli monitor_cli.c)
|
||||
target_link_libraries(stellar-cli libevent-static cjson-static linenoise sds ringbuf monitor)
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/stellar-cli DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ COMPONENT EXECUTABLE)
|
||||
|
||||
# add_subdirectory(stellar-dump)
|
||||
|
||||
#tcpdump patch
|
||||
set (STELLAR-DUMP-PATCH-FILE ${CMAKE_SOURCE_DIR}/tools/monitor/stellar-dump.patch)
|
||||
set (STELLAR-DUMP-PATCH-CMD ${CMAKE_SOURCE_DIR}/tools/monitor/stellar-dump-update.sh)
|
||||
ExternalProject_Add(tcpdump PREFIX tcpdump
|
||||
URL ${CMAKE_SOURCE_DIR}/vendors/tcpdump-4.99.4.tar.gz
|
||||
URL_MD5 4f2d4a8a5fab017e5ebda156bfc86378
|
||||
PATCH_COMMAND sh -c "chmod +x ${STELLAR-DUMP-PATCH-CMD} && ${STELLAR-DUMP-PATCH-CMD} ${CMAKE_CURRENT_BINARY_DIR}/tcpdump/src/tcpdump ${STELLAR-DUMP-PATCH-FILE}"
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DWITH_CRYPTO=OFF -DSTELLAR_SOURCE_DIR=${CMAKE_SOURCE_DIR} -DSTELLAR_BINARY_DIR=${CMAKE_BINARY_DIR} -DASAN_OPTION=${ASAN_OPTION}
|
||||
)
|
||||
add_dependencies(tcpdump libevent)
|
||||
ExternalProject_Get_Property(tcpdump INSTALL_DIR)
|
||||
install(PROGRAMS ${INSTALL_DIR}/src/tcpdump-build/stellar-dump DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ COMPONENT EXECUTABLE)
|
||||
557
tools/monitor/monitor_cli.c
Normal file
557
tools/monitor/monitor_cli.c
Normal file
@@ -0,0 +1,557 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <evhttp.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "stellar/monitor.h"
|
||||
#include "linenoise/linenoise.h"
|
||||
#include "monitor_cmd_assistant.h"
|
||||
#include "monitor_private.h"
|
||||
#include "monitor/monitor_utils.h"
|
||||
|
||||
static char g_stellar_cli_prompt[128]; /* prompt pattern: cli@ip:port> */
|
||||
static const char *g_stellar_monitor_history_file = ".stellar_cli_history.txt";
|
||||
static const char *g_stellar_monitor_version = "stellar-cli v1.0";
|
||||
static int g_stm_cli_noninteractive = 0;
|
||||
static const char *g_stm_cli_noninteractive_cmd_line = NULL;
|
||||
static const char *g_stm_cli_ipaddr_str = STM_SERVER_LISTEN_IP;
|
||||
static unsigned short g_stm_cli_port_host = STM_SERVER_LISTEN_PORT;
|
||||
static struct libevent_http_client *g_evh_client;
|
||||
static struct stm_cmd_assistant *g_stm_cli_aide;
|
||||
static int g_stm_cli_connect_timeout = STM_REQUEST_TIMEOUT;
|
||||
|
||||
static struct monitor_cli_args g_cli_args[4] = {
|
||||
{"-i", "--ip", 1, 0, NULL},
|
||||
{"-p", "--port", 1, 0, NULL},
|
||||
{"-t", "--timeout", 1, 0, NULL},
|
||||
{"-e", "--exec", 1, 1, NULL},
|
||||
};
|
||||
|
||||
struct stm_cmd_parser
|
||||
{
|
||||
sds raw_cmd_line; // need be free
|
||||
int argc;
|
||||
sds *argv;
|
||||
};
|
||||
|
||||
struct stm_builtin_cmd_compose
|
||||
{
|
||||
const char *cmd_name;
|
||||
monitor_cmd_cb *cmd_cb;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
struct libevent_http_client
|
||||
{
|
||||
struct event_base *base;
|
||||
struct evhttp_connection *conn;
|
||||
struct evhttp_request *req;
|
||||
enum stm_http_response_code response_code;
|
||||
char *response_cstr;
|
||||
};
|
||||
|
||||
static int (*g_response_handler)(struct libevent_http_client *evh_client);
|
||||
|
||||
static void cli_evhttp_free(void)
|
||||
{
|
||||
evhttp_connection_free(g_evh_client->conn);
|
||||
event_base_free(g_evh_client->base);
|
||||
FREE(g_evh_client);
|
||||
}
|
||||
|
||||
static void stm_cli_args_free(struct stm_cmd_parser *cmd_args)
|
||||
{
|
||||
if (NULL != cmd_args)
|
||||
{
|
||||
sdsfree(cmd_args->raw_cmd_line);
|
||||
sdsfreesplitres(cmd_args->argv, cmd_args->argc);
|
||||
FREE(cmd_args);
|
||||
}
|
||||
}
|
||||
|
||||
static struct monitor_reply *stm_cli_builtin_exit(UNUSED struct stellar_monitor *monitor, UNUSED int argc, UNUSED char *argv[], void *arg)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(g_cli_args) / sizeof(struct monitor_cli_args); i++)
|
||||
{
|
||||
sdsfree(g_cli_args[i].value);
|
||||
}
|
||||
stm_cli_args_free((struct stm_cmd_parser *)arg);
|
||||
stm_cmd_assistant_free(g_stm_cli_aide);
|
||||
cli_evhttp_free();
|
||||
exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void signal_handler(int signo)
|
||||
{
|
||||
if (signo == SIGINT)
|
||||
{
|
||||
stm_cli_builtin_exit(NULL, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct monitor_reply *stm_cli_builtin_clear(UNUSED struct stellar_monitor *monitor, UNUSED int argc, UNUSED char *argv[], UNUSED void *arg)
|
||||
{
|
||||
linenoiseClearScreen();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct stm_builtin_cmd_compose g_stm_cli_builtin_commands[] = {
|
||||
{"q", stm_cli_builtin_exit, "cause the shell to exit"},
|
||||
{"quit", stm_cli_builtin_exit, "cause the shell to exit"},
|
||||
{"exit", stm_cli_builtin_exit, "cause the shell to exit"},
|
||||
{"clear", stm_cli_builtin_clear, "clear the terminal screen"},
|
||||
{NULL, NULL, NULL}};
|
||||
|
||||
static void evhttp_conn_close_cb(UNUSED struct evhttp_connection *conn, UNUSED void *arg)
|
||||
{
|
||||
snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt), "not connected>");
|
||||
}
|
||||
|
||||
static void evhttp_request_error_cb(enum evhttp_request_error errnum, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
switch (errnum)
|
||||
{
|
||||
case EVREQ_HTTP_TIMEOUT:
|
||||
g_evh_client->response_code = STM_HTTP_408_REQUEST_TIMEOUT;
|
||||
break;
|
||||
case EVREQ_HTTP_INVALID_HEADER:
|
||||
g_evh_client->response_code = STM_HTTP_403_FORBIDDEN;
|
||||
break;
|
||||
case EVREQ_HTTP_DATA_TOO_LONG:
|
||||
g_evh_client->response_code = STM_HTTP_413_PAYLOAD_TOO_LARGE;
|
||||
break;
|
||||
case EVREQ_HTTP_EOF:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void evhttp_response_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (req == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
struct evbuffer *input_buffer = evhttp_request_get_input_buffer(req);
|
||||
size_t evbuf_len = evbuffer_get_length(input_buffer);
|
||||
if (NULL == input_buffer || 0 == evbuf_len)
|
||||
{
|
||||
return;
|
||||
}
|
||||
g_evh_client->response_cstr = (char *)calloc(1, evbuf_len + 1);
|
||||
evbuffer_remove(input_buffer, g_evh_client->response_cstr, evbuf_len);
|
||||
g_evh_client->response_code = STM_HTTP_200_OK;
|
||||
// terminate event_base_dispatch()
|
||||
event_base_loopbreak(g_evh_client->base);
|
||||
}
|
||||
|
||||
static struct libevent_http_client *evhttp_client_new(const char *server_ip, unsigned short server_port)
|
||||
{
|
||||
struct libevent_http_client *evh_client =
|
||||
(struct libevent_http_client *)calloc(1, sizeof(struct libevent_http_client));
|
||||
|
||||
evh_client->base = event_base_new();
|
||||
evh_client->conn = evhttp_connection_base_new(evh_client->base, NULL,
|
||||
server_ip, server_port);
|
||||
evhttp_connection_set_timeout(evh_client->conn, g_stm_cli_connect_timeout);
|
||||
return evh_client;
|
||||
}
|
||||
|
||||
static int evhttp_client_request_new(struct libevent_http_client *evh_client)
|
||||
{
|
||||
evh_client->req = evhttp_request_new(evhttp_response_cb, evh_client);
|
||||
evhttp_request_set_error_cb(evh_client->req, evhttp_request_error_cb);
|
||||
evhttp_connection_set_closecb(evh_client->conn, evhttp_conn_close_cb, evh_client->req);
|
||||
evh_client->response_cstr = NULL;
|
||||
evh_client->response_code = STM_HTTP_204_NO_CONTENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void evhttp_client_add_header(struct libevent_http_client *evh_client, const char *key, const char *value)
|
||||
{
|
||||
struct evkeyvalq *output_headers = evhttp_request_get_output_headers(evh_client->req);
|
||||
evhttp_add_header(output_headers, key, value);
|
||||
}
|
||||
|
||||
static void evhttp_client_add_uri(struct libevent_http_client *evh_client,
|
||||
enum evhttp_cmd_type type, const char *uri)
|
||||
{
|
||||
evhttp_make_request(evh_client->conn, evh_client->req, type, uri);
|
||||
}
|
||||
|
||||
static int default_response_handler(struct libevent_http_client *evh_client)
|
||||
{
|
||||
if (evh_client->response_code != STM_HTTP_200_OK || evh_client->response_cstr == NULL)
|
||||
{
|
||||
snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt), "not connected>");
|
||||
fprintf(stderr, "ERR failed to connect to %s:%u\n",
|
||||
g_stm_cli_ipaddr_str, g_stm_cli_port_host);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s", evh_client->response_cstr);
|
||||
fflush(stdout);
|
||||
FREE(evh_client->response_cstr);
|
||||
snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt),
|
||||
"cli@%s:%u>", g_stm_cli_ipaddr_str, g_stm_cli_port_host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int command_json_parse_handler(struct libevent_http_client *evh_client)
|
||||
{
|
||||
if (evh_client->response_code != STM_HTTP_200_OK || evh_client->response_cstr == NULL)
|
||||
{
|
||||
snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt), "not connected>");
|
||||
fprintf(stderr, "ERR failed to connect to %s:%u\n",
|
||||
g_stm_cli_ipaddr_str, g_stm_cli_port_host);
|
||||
return -1;
|
||||
}
|
||||
if (stm_cmd_assistant_json_load(g_stm_cli_aide, evh_client->response_cstr) < 0)
|
||||
{
|
||||
fprintf(stderr, "ERR failed to synchronize command info with the monitor server\n");
|
||||
return -1;
|
||||
}
|
||||
FREE(evh_client->response_cstr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm_cli_usage(void)
|
||||
{
|
||||
printf("%s\r\n", g_stellar_monitor_version);
|
||||
printf("Usage:\r\n");
|
||||
printf(" %s [OPTIONS] [ -e command [arg [arg ...]]]\r\n", "stellar-cli");
|
||||
printf("\t%s %-6s %s\r\n", "-i", "--ip", "stellar monitor server ip address");
|
||||
printf("\t%s %-6s %s\r\n", "-p", "--port", "stellar monitor server port");
|
||||
printf("\t%s %-6s %s\r\n", "-e", "--exec", "non-interactive mode, exit after executing command");
|
||||
printf("\t%s %-6s %s\r\n", "-t", "--timeout", "maximum time(sec) allowed for connecting to server");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int stm_cli_exec_builtin_cmd(struct stm_cmd_parser *cmd_args)
|
||||
{
|
||||
const char *cli_cmd_name = cmd_args->argv[0];
|
||||
size_t raw_cmd_len = strlen(cli_cmd_name);
|
||||
for (int i = 0; g_stm_cli_builtin_commands[i].cmd_name != NULL; i++)
|
||||
{
|
||||
if (stm_strncasecmp_exactly(g_stm_cli_builtin_commands[i].cmd_name,
|
||||
cli_cmd_name, raw_cmd_len) == 0)
|
||||
{
|
||||
g_stm_cli_builtin_commands[i].cmd_cb(NULL, cmd_args->argc, cmd_args->argv, (void *)cmd_args);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sds stm_cli_build_RESTful_url(struct stm_cmd_parser *cmd_args)
|
||||
{
|
||||
sds url;
|
||||
char restful_path[256] = {0};
|
||||
snprintf(restful_path, sizeof(restful_path), "/%s/%s", STM_RESTFUL_VERSION, STM_RESTFUL_RESOURCE);
|
||||
url = sdsempty();
|
||||
url = sdscatprintf(url, "%s?%s=", restful_path, STM_RESTFUL_URI_CMD_KEY);
|
||||
for (int i = 0; i < cmd_args->argc; i++)
|
||||
{
|
||||
url = sdscat(url, cmd_args->argv[i]);
|
||||
if (i < cmd_args->argc - 1)
|
||||
{
|
||||
url = sdscat(url, " "); // add blank space
|
||||
}
|
||||
}
|
||||
char *encoded_url_str = stm_http_url_encode(url);
|
||||
sds encoded_url = sdsnew(encoded_url_str);
|
||||
sdsfree(url);
|
||||
free(encoded_url_str);
|
||||
return encoded_url;
|
||||
}
|
||||
|
||||
static int stm_cli_evhttp_run_cmd(struct stm_cmd_parser *cmd_args)
|
||||
{
|
||||
evhttp_client_request_new(g_evh_client);
|
||||
evhttp_client_add_header(g_evh_client, "Connection", "keep-alive");
|
||||
evhttp_client_add_header(g_evh_client, "Content-Length", "0");
|
||||
sds url = stm_cli_build_RESTful_url(cmd_args);
|
||||
evhttp_client_add_uri(g_evh_client, EVHTTP_REQ_GET, url);
|
||||
int ret = event_base_dispatch(g_evh_client->base);
|
||||
sdsfree(url);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* call remote command use RESTful */
|
||||
static int stm_cli_exec_rpc_cmd(UNUSED const char *raw_cmd_line, struct stm_cmd_parser *cmd_args)
|
||||
{
|
||||
int ret = stm_cli_evhttp_run_cmd(cmd_args);
|
||||
g_response_handler(g_evh_client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct stm_cmd_parser *stm_cli_parse_cmd_line(const char *line)
|
||||
{
|
||||
struct stm_cmd_parser *cmd_args =
|
||||
(struct stm_cmd_parser *)calloc(1, sizeof(struct stm_cmd_parser));
|
||||
cmd_args->raw_cmd_line = sdsnew(line);
|
||||
cmd_args->argv = sdssplitargs(line, &cmd_args->argc);
|
||||
return cmd_args;
|
||||
}
|
||||
|
||||
static void stm_cli_exec_cmd(const char *raw_line)
|
||||
{
|
||||
struct stm_cmd_parser *cmd_args = stm_cli_parse_cmd_line(raw_line);
|
||||
if (stm_cli_exec_builtin_cmd(cmd_args))
|
||||
{
|
||||
goto fun_exit;
|
||||
}
|
||||
stm_cli_exec_rpc_cmd(raw_line, cmd_args);
|
||||
|
||||
fun_exit:
|
||||
stm_cli_args_free(cmd_args);
|
||||
return;
|
||||
}
|
||||
|
||||
static int stm_cli_builtin_help(const char *line)
|
||||
{
|
||||
int argc = 0;
|
||||
int is_help_cmd = 0;
|
||||
sds *array = sdssplitargs(line, &argc);
|
||||
|
||||
if (argc != 1)
|
||||
{
|
||||
sdsfreesplitres(array, argc);
|
||||
return 0;
|
||||
}
|
||||
if ((strcasecmp(array[argc - 1], "help") == 0) || (strcasecmp(array[argc - 1], "--help") == 0) || (strcasecmp(array[argc - 1], "-h") == 0) || (strcasecmp(array[argc - 1], "/?") == 0) || (strcasecmp(array[argc - 1], "?") == 0))
|
||||
{
|
||||
is_help_cmd = 1;
|
||||
}
|
||||
if (is_help_cmd == 1)
|
||||
{
|
||||
stm_cli_exec_cmd("show command brief");
|
||||
}
|
||||
sdsfreesplitres(array, argc);
|
||||
return is_help_cmd;
|
||||
}
|
||||
|
||||
static void stm_cli_register_builtin_cmd(void)
|
||||
{
|
||||
for (int i = 0; g_stm_cli_builtin_commands[i].cmd_name != NULL; i++)
|
||||
{
|
||||
stm_cmd_assistant_register_cmd(g_stm_cli_aide,
|
||||
g_stm_cli_builtin_commands[i].cmd_name,
|
||||
g_stm_cli_builtin_commands[i].cmd_cb,
|
||||
NULL, "readonly", "<cr>",
|
||||
g_stm_cli_builtin_commands[i].description);
|
||||
}
|
||||
}
|
||||
|
||||
static void stm_cli_run(void)
|
||||
{
|
||||
char *line;
|
||||
/* Load history from file. The history file is just a plain text file
|
||||
* where entries are separated by newlines. */
|
||||
linenoiseHistoryLoad(g_stellar_monitor_history_file); /* Load the history at startup */
|
||||
/* Non-interactive mode */
|
||||
if (g_stm_cli_noninteractive)
|
||||
{
|
||||
stm_cli_exec_cmd(g_stm_cli_noninteractive_cmd_line);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Synchronize with the monitor server on boot up */
|
||||
g_response_handler = command_json_parse_handler;
|
||||
stm_cli_exec_cmd(STM_CLIENT_SERVER_SYNC_CMD);
|
||||
g_response_handler = default_response_handler;
|
||||
|
||||
/* register builtin command after synchronization */
|
||||
stm_cli_register_builtin_cmd();
|
||||
|
||||
/* Interactive mode */
|
||||
while (1)
|
||||
{
|
||||
line = linenoise(g_stellar_cli_prompt);
|
||||
if (line && strlen(line) > 0)
|
||||
{
|
||||
if (stm_cli_builtin_help(line) == 0)
|
||||
{
|
||||
stm_cli_exec_cmd(line);
|
||||
}
|
||||
fflush(stdout);
|
||||
linenoiseHistoryAdd(line);
|
||||
}
|
||||
FREE(line);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *stm_cli_short_options = "he:i:p:t:";
|
||||
static const struct option stm_cli_long_options[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"ip", required_argument, NULL, 'i'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"exec", required_argument, NULL, 'e'},
|
||||
{"timeout", required_argument, NULL, 't'},
|
||||
};
|
||||
|
||||
static int stm_cli_check_args(int argc, char *_argv[])
|
||||
{
|
||||
int c, ret = 0;
|
||||
char **argv_tmp = CALLOC(char *, argc + 1);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
argv_tmp[i] = _argv[i];
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
c = getopt_long(argc, argv_tmp, stm_cli_short_options, stm_cli_long_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
stm_cli_usage();
|
||||
break;
|
||||
case 'i':
|
||||
case 'p':
|
||||
case 'e':
|
||||
case 't':
|
||||
break;
|
||||
case '?': /* invalid or unknown option */
|
||||
ret = -1;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FREE(argv_tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm_cli_evhttp_init(void)
|
||||
{
|
||||
g_evh_client = evhttp_client_new(g_stm_cli_ipaddr_str, g_stm_cli_port_host);
|
||||
assert(g_evh_client != NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cli_linenoise_completion_cb(const char *line, linenoiseCompletions *lc)
|
||||
{
|
||||
stm_cmd_assistant_input_line(g_stm_cli_aide, line, (void *)lc);
|
||||
}
|
||||
|
||||
static char *cli_linenoise_hints_cb(const char *line, int *color, int *bold)
|
||||
{
|
||||
char *hints = (char *)stm_cmd_assistant_input_line_for_hints(g_stm_cli_aide, line);
|
||||
if (NULL == hints)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
sds tmp = sdsnew(" "); // add a blank space before hints, easy to input the next command
|
||||
tmp = sdscat(tmp, hints);
|
||||
*color = STM_CLI_CMD_HINTS_COLOR;
|
||||
*bold = STM_CLI_CMD_HINTS_BOLD;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void cli_linenoise_free_hints_cb(void *arg)
|
||||
{
|
||||
sdsfree((sds)arg);
|
||||
}
|
||||
|
||||
void cli_assistant_completion_cb(void *arg, const char *candidate_completion)
|
||||
{
|
||||
linenoiseCompletions *lc = (linenoiseCompletions *)arg;
|
||||
linenoiseAddCompletion(lc, candidate_completion);
|
||||
}
|
||||
|
||||
static int stm_assistant_init(void)
|
||||
{
|
||||
g_stm_cli_aide = stm_cmd_assistant_new();
|
||||
if (NULL == g_stm_cli_aide)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
/* Set the completion callback. This will be called every time the
|
||||
* user uses the <tab> key. */
|
||||
linenoiseSetCompletionCallback(cli_linenoise_completion_cb);
|
||||
stm_cmd_assistant_set_completion_cb(g_stm_cli_aide, cli_assistant_completion_cb);
|
||||
linenoiseSetHintsCallback(cli_linenoise_hints_cb);
|
||||
linenoiseSetFreeHintsCallback(cli_linenoise_free_hints_cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_cli_parse_args(int argc, char *argv[])
|
||||
{
|
||||
if (stm_cli_check_args(argc, argv) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (monitor_util_parse_cmd_args(argc, (const char **)argv, g_cli_args,
|
||||
sizeof(g_cli_args) / sizeof(struct monitor_cli_args)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (g_cli_args[0].value != NULL)
|
||||
{
|
||||
g_stm_cli_ipaddr_str = g_cli_args[0].value;
|
||||
}
|
||||
int tmp_val;
|
||||
if (g_cli_args[1].value != NULL)
|
||||
{
|
||||
tmp_val = atoi(g_cli_args[1].value);
|
||||
if (tmp_val <= 0 || tmp_val > 65535)
|
||||
{
|
||||
fprintf(stderr, "invalid port: %s\n", g_cli_args[1].value);
|
||||
return -1;
|
||||
}
|
||||
g_stm_cli_port_host = (unsigned short)tmp_val;
|
||||
}
|
||||
if (g_cli_args[2].value != NULL)
|
||||
{
|
||||
tmp_val = atoi(g_cli_args[2].value);
|
||||
if (tmp_val <= 0)
|
||||
{
|
||||
fprintf(stderr, "invalid timeout: %s\n", g_cli_args[2].value);
|
||||
return -1;
|
||||
}
|
||||
g_stm_cli_connect_timeout = tmp_val;
|
||||
}
|
||||
if (g_cli_args[3].value != NULL)
|
||||
{
|
||||
g_stm_cli_noninteractive = 1;
|
||||
g_stm_cli_noninteractive_cmd_line = g_cli_args[3].value;
|
||||
}
|
||||
snprintf(g_stellar_cli_prompt, sizeof(g_stellar_cli_prompt),
|
||||
"cli@%s:%u>", g_stm_cli_ipaddr_str, g_stm_cli_port_host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (stm_cli_parse_args(argc, argv) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (stm_assistant_init() < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (stm_cli_evhttp_init() < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
g_response_handler = default_response_handler;
|
||||
signal(SIGINT, signal_handler);
|
||||
stm_cli_run();
|
||||
return 0;
|
||||
}
|
||||
12
tools/monitor/stellar-dump-update.sh
Executable file
12
tools/monitor/stellar-dump-update.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
SOURCE_DIR=$1
|
||||
PATCH_FILE=$2
|
||||
PATCH_STATUS=`head $SOURCE_DIR/tcpdump.c | grep "PATCH FOR STELLAR-DUMP"`
|
||||
|
||||
if [ -z "$PATCH_STATUS" ]
|
||||
then
|
||||
echo "starting patch tcpdump..."
|
||||
patch -N -p1 -d $SOURCE_DIR -i $PATCH_FILE
|
||||
else
|
||||
echo "alread patched, skip... "
|
||||
fi
|
||||
11400
tools/monitor/stellar-dump.patch
Normal file
11400
tools/monitor/stellar-dump.patch
Normal file
File diff suppressed because it is too large
Load Diff
2
vendors/CMakeLists.txt
vendored
2
vendors/CMakeLists.txt
vendored
@@ -85,7 +85,7 @@ ExternalProject_Add(libevent
|
||||
PREFIX libevent
|
||||
URL ${CMAKE_CURRENT_SOURCE_DIR}/libevent-2.1.12-stable.tar.gz
|
||||
URL_MD5 b5333f021f880fe76490d8a799cd79f4
|
||||
CMAKE_ARGS -DEVENT__DISABLE_OPENSSL=ON -DEVENT__DISABLE_BENCHMARK=ON -DBUILD_TESTING=OFF -DEVENT__DISABLE_TESTS=ON -DEVENT__LIBRARY_TYPE=STATIC
|
||||
CMAKE_ARGS -DEVENT__DISABLE_OPENSSL=ON -DEVENT__DISABLE_BENCHMARK=ON -DBUILD_TESTING=OFF -DEVENT__DISABLE_TESTS=ON -DEVENT__LIBRARY_TYPE=BOTH -DCMAKE_C_FLAGS="-fPIC"
|
||||
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
||||
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
)
|
||||
|
||||
BIN
vendors/tcpdump-4.99.4.tar.gz
vendored
Normal file
BIN
vendors/tcpdump-4.99.4.tar.gz
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user