rebase onto develop-2.0

This commit is contained in:
lijia
2024-10-18 16:47:51 +08:00
parent 99a68d5c9e
commit e734af76d8
66 changed files with 22616 additions and 6 deletions

View File

@@ -52,6 +52,7 @@ stages:
--suppress=unreachableCode
--suppress=internalAstError
--suppress=integerOverflow
--suppress=*:${CI_PROJECT_DIR}/infra/monitor/stellar-dump/*
tags:
- share

View File

@@ -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)

View File

@@ -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
View File

@@ -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
View 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

File diff suppressed because it is too large Load Diff

77
deps/linenoise/linenoise.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

285
deps/sds/sds.h vendored Normal file
View 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
View 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
View 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
View 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

View File

@@ -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)

View 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)

View 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})

View 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;
}

View 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, &register_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, &register_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, &register_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;
}

View 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

View 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

View 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;
}
}

View 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
View 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);
}

View 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);

View 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;
}

View 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

View 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);

View 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);
}

View 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;
}

View 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], &copy_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

View 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
View File

@@ -0,0 +1,10 @@
VERS_3.0{
global:
extern "C" {
stellar_monitor_*;
monitor_*;
stm_*;
stellar_module_get_monitor;
};
local: *;
};

View File

@@ -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

View File

@@ -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];
}

View File

@@ -7,6 +7,7 @@ extern "C"
#include "tuple.h"
#include "stellar/session.h"
#include "session_manager_cfg.h"
enum session_scan_flags
{

View File

@@ -74,5 +74,7 @@ global:
lpi_plus_exit;
lpi_plus_appid_subscribe;
monitor_on_init;
monitor_on_exit;
local: *;
};

View File

@@ -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)

View 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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.llhttp_parser, request, strlen(request));
ASSERT_TRUE(lerr != HPE_OK);
}
TEST(HTTP_llhttp, request_uncompleted)
{
struct gtest_http_parm para = {};
gtest_llhttp_init(&para.llhttp_parser, HTTP_REQUEST, &para.settings);
const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length:";
enum llhttp_errno lerr = llhttp_execute(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.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(&para.llhttp_parser, HTTP_REQUEST, &para.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(&para.llhttp_parser, response, strlen(response));
ASSERT_TRUE(lerr != HPE_OK);
}
TEST(HTTP_llhttp, response_uncompleted)
{
struct gtest_http_parm para = {};
gtest_llhttp_init(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\n\r\nxxx";
enum llhttp_errno lerr = llhttp_execute(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.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(&para.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(&para.llhttp_parser, HTTP_RESPONSE, &para.settings);
const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n";
enum llhttp_errno lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
ASSERT_EQ(lerr, HPE_OK);
lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
ASSERT_EQ(lerr, HPE_OK);
lerr = llhttp_execute(&para.llhttp_parser, response, strlen(response));
ASSERT_EQ(lerr, HPE_OK);
llhttp_reset(&para.llhttp_parser);
memset(&para.count, 0, sizeof(para.count));
response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n";
lerr = llhttp_execute(&para.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();
}

View 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)

View 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, &register_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, &register_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, &register_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, &register_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, &register_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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}

View 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;
}

View 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
View 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;
}

View 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;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
tools/CMakeLists.txt Normal file
View File

@@ -0,0 +1 @@
add_subdirectory(monitor)

View 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
View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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

Binary file not shown.