Compare commits
35 Commits
dev-2.0
...
shm_ring_q
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
451af3b929 | ||
|
|
3d6d7b909a | ||
|
|
fac6124b6e | ||
|
|
1655537e0d | ||
|
|
6bf1bc201c | ||
|
|
54a2f747d5 | ||
|
|
1898fb9eb3 | ||
|
|
db8b5e259e | ||
|
|
ba9dd722b2 | ||
|
|
09b22fd49a | ||
|
|
0fb5b8adea | ||
|
|
5e17fced23 | ||
|
|
8267c9712e | ||
|
|
89899d2d0a | ||
|
|
a5bffd2fe7 | ||
|
|
dab9b5c03d | ||
|
|
c059e7479e | ||
|
|
e13e857381 | ||
|
|
fada178df2 | ||
|
|
4531d74c80 | ||
|
|
775982f2d5 | ||
|
|
7a88ab75ec | ||
|
|
b94706ac06 | ||
|
|
3421f97f95 | ||
|
|
93c07a240b | ||
|
|
0ce7424fa1 | ||
|
|
21b84aa885 | ||
|
|
30f2094627 | ||
|
|
7faf202791 | ||
|
|
af7c375634 | ||
|
|
d7b64b4577 | ||
|
|
65ed1c3a92 | ||
|
|
08fd550822 | ||
|
|
c0c6a97a13 | ||
|
|
20f3fb58b7 |
@@ -36,11 +36,16 @@ include_directories(${PROJECT_SOURCE_DIR}/inc/)
|
||||
add_subdirectory(zlog)
|
||||
|
||||
# Shared Library Output
|
||||
add_library(${lib_name}_shared SHARED src/MESA_handle_logger.c)
|
||||
add_library(${lib_name}_shared SHARED src/MESA_handle_logger.c src/MESA_shm_ring_queue.c src/portable_snprintf/snprintf.c)
|
||||
target_link_libraries(${lib_name}_shared zlog_static)
|
||||
set_target_properties(${lib_name}_shared PROPERTIES LINK_FLAGS
|
||||
"-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map")
|
||||
target_link_libraries(${lib_name}_shared pthread)
|
||||
target_compile_options(${lib_name}_shared PRIVATE -DHAVE_SNPRINTF -DPREFER_PORTABLE_SNPRINTF -DSNPRINTF_LONGLONG_SUPPORT)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(DEFINED MESA_SHARED_INSTALL_DIR)
|
||||
set_target_properties(${lib_name}_shared PROPERTIES OUTPUT_NAME ${lib_name} LIBRARY_OUTPUT_DIRECTORY ${MESA_SHARED_INSTALL_DIR})
|
||||
@@ -52,9 +57,10 @@ set_target_properties(${lib_name}_shared PROPERTIES VERSION ${LIB_MAJOR_VERSION}
|
||||
set_target_properties(${lib_name}_shared PROPERTIES SOVERSION ${LIB_MAJOR_VERSION})
|
||||
|
||||
# static Library Output
|
||||
add_library(${lib_name}_static STATIC src/MESA_handle_logger.c)
|
||||
add_library(${lib_name}_static STATIC src/MESA_handle_logger.c src/MESA_shm_ring_queue.c src/portable_snprintf/snprintf.c)
|
||||
target_link_libraries(${lib_name}_static zlog_static)
|
||||
set_target_properties(${lib_name}_static PROPERTIES OUTPUT_NAME ${lib_name})
|
||||
target_compile_options(${lib_name}_static PRIVATE -DHAVE_SNPRINTF -DPREFER_PORTABLE_SNPRINTF -DSNPRINTF_LONGLONG_SUPPORT)
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX /opt/MESA)
|
||||
|
||||
@@ -69,4 +75,9 @@ install(FILES conf/MESA_handle_logger.conf DESTINATION /usr/lib/tmpfiles.d/ COMP
|
||||
add_executable(${lib_name}_demo demo/test_handle_logger.c)
|
||||
target_link_libraries(${lib_name}_demo pthread ${lib_name}_shared)
|
||||
|
||||
|
||||
add_executable(${lib_name}_consumer shm_consumer/MESA_shm_consumer.c)
|
||||
target_link_libraries(${lib_name}_consumer ${lib_name}_shared)
|
||||
target_compile_options(${lib_name}_consumer PRIVATE -DCONSUMER_DEBUG)
|
||||
|
||||
include(Package)
|
||||
|
||||
5
Makefile
5
Makefile
@@ -6,9 +6,12 @@
|
||||
all:
|
||||
cd src && $(MAKE)
|
||||
cd demo && $(MAKE)
|
||||
cd shm_consumer && $(MAKE)
|
||||
|
||||
clean:
|
||||
#cd demo && $(MAKE) clean
|
||||
cd demo && $(MAKE) clean
|
||||
cd src && $(MAKE) clean
|
||||
cd shm_consumer && $(MAKE) clean
|
||||
|
||||
opt:
|
||||
$(MAKE) all
|
||||
|
||||
@@ -7,8 +7,8 @@ CFLAGS= -g3 -Wall -fPIC -O -Werror
|
||||
CFLAGS+=-I../inc/
|
||||
|
||||
#LIB=-L../lib/
|
||||
LIB=../lib/libMESA_handle_logger.a
|
||||
LIB+=-lpthread
|
||||
LIB=../lib/libMESA_handle_logger.a ../lib/libMESA_snprintf.a
|
||||
LIB+=-lpthread -lzlog
|
||||
|
||||
LIB_FILE=$(wildcard ../lib/*.a)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
void *sample_handle = NULL;
|
||||
void *test_handle = NULL;
|
||||
@@ -19,75 +20,92 @@ int g_thread_num = 0;
|
||||
const char *g_zlog_conf = NULL;
|
||||
volatile long g_start_time = 0;
|
||||
volatile long g_end_time = 0;
|
||||
struct test_A{
|
||||
int a;
|
||||
char *str;
|
||||
long long b;
|
||||
};
|
||||
int MESA_fmt_handler_A(void *info, char *buf, int maxlen)
|
||||
{
|
||||
struct test_A *obj = (struct test_A *)info;
|
||||
int n = snprintf(buf, maxlen, "a=%d,s=%s,b=%lld", obj->a, obj->str, obj->b);
|
||||
return n < maxlen?n:maxlen - 1;
|
||||
}
|
||||
|
||||
void call_logger(int log_num, int thread_num)
|
||||
{
|
||||
int i = 0;
|
||||
struct timespec start, end;
|
||||
long start_time, end_time, time_cost;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
start_time = start.tv_sec*1000000 + start.tv_nsec/1000;
|
||||
if(g_start_time == 0)
|
||||
|
||||
struct test_A a;
|
||||
a.a = 10;
|
||||
a.str = "hello";
|
||||
a.b = 123456;
|
||||
int i = 0;
|
||||
struct timespec start, end;
|
||||
long start_time, end_time, time_cost;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
start_time = start.tv_sec*1000000 + start.tv_nsec/1000;
|
||||
if(g_start_time == 0)
|
||||
{
|
||||
g_start_time = start_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_start_time > start_time)
|
||||
{
|
||||
g_start_time = start_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_start_time > start_time)
|
||||
{
|
||||
g_start_time = start_time;
|
||||
}
|
||||
}
|
||||
for(i = 0; i < log_num; i++)
|
||||
{
|
||||
MESA_handle_runtime_log(sample_handle, RLOG_LV_DEBUG, "sample", "sample_handle MESA_handle_runtime_log, i = %d, thread_num = %d", i, thread_num);
|
||||
//sleep(1);
|
||||
MESA_handle_runtime_log(test_handle, RLOG_LV_INFO, "test", "test_handle MESA_handle_runtime_log, i = %d, thread_num = %d", i, thread_num);
|
||||
//MESA_HANDLE_RUNTIME_LOG(sample_handle, RLOG_LV_FATAL, "sample", "sample_handle RUNTIEM_LOG test, i = %d, thread_num = %d", i, thread_num);
|
||||
//sleep(1);
|
||||
//MESA_HANDLE_RUNTIME_LOG(test_handle, RLOG_LV_FATAL, "test", "test_handle RUNTIEM_LOG test, i = %d, thread_num = %d", i, thread_num);
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
end_time = end.tv_sec*1000000 + end.tv_nsec/1000;
|
||||
if(g_end_time == 0)
|
||||
}
|
||||
for(i = 0; i < log_num; i++)
|
||||
{
|
||||
//MESA_handle_runtime_log(sample_handle, RLOG_LV_DEBUG, "sample", "sample_handle MESA_handle_runtime_log, i = %d, thread_num = %d", i, thread_num);
|
||||
//sleep(1);
|
||||
//MESA_handle_runtime_log(test_handle, RLOG_LV_INFO, "test", "test_handle MESA_handle_runtime_log, i = %d, thread_num = %d", i, thread_num);
|
||||
MESA_HANDLE_RUNTIME_LOG(sample_handle, RLOG_LV_DEBUG, "sample", "sample_handle RUNTIEM_LOG test, i = %d, thread_num = %d", i, thread_num);
|
||||
//sleep(1);
|
||||
MESA_HANDLE_RUNTIME_LOG(test_handle, RLOG_LV_FATAL, "test", "test_handle RUNTIEM_LOG test, i = %d, thread_num = %d", i, thread_num);
|
||||
MESA_HANDLE_RUNTIME_LOG(sample_handle, RLOG_LV_FATAL, "sample", "sample_handle RUNTIEM_LOG test, i = %d, thread_num = %d,struct a =%A", i, thread_num, &a);
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
end_time = end.tv_sec*1000000 + end.tv_nsec/1000;
|
||||
if(g_end_time == 0)
|
||||
{
|
||||
g_end_time = end_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_end_time < end_time)
|
||||
{
|
||||
g_end_time = end_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_end_time < end_time)
|
||||
{
|
||||
g_end_time = end_time;
|
||||
}
|
||||
}
|
||||
time_cost = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000 ;
|
||||
printf("THREAD %d write %d log using %ld us, avg speed %f /s, %ld -> %ld\n", thread_num, log_num, time_cost, ((float)log_num/(float)time_cost)*1000000, start_time, end_time);
|
||||
return;
|
||||
}
|
||||
}
|
||||
time_cost = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000 ;
|
||||
printf("THREAD %d write %d log using %ld us, avg speed %f /s, %ld -> %ld\n", thread_num, log_num, time_cost, ((float)log_num/(float)time_cost)*1000000, start_time, end_time);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void *thread_logger(void *arg)
|
||||
{
|
||||
int thread_num = (int)(unsigned long long)arg;
|
||||
printf("thread %d created! \n", thread_num);
|
||||
call_logger(g_log_num, thread_num);
|
||||
printf("thread %d finished! \n", thread_num);
|
||||
return NULL;
|
||||
int thread_num = (int)(unsigned long long)arg;
|
||||
printf("thread %d created! \n", thread_num);
|
||||
call_logger(g_log_num, thread_num);
|
||||
printf("thread %d finished! \n", thread_num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sig_int_handler(int sig)
|
||||
{
|
||||
printf("ctrl+c recviced!\n");
|
||||
MESA_destroy_runtime_log_handle(sample_handle);
|
||||
MESA_destroy_runtime_log_handle(test_handle);
|
||||
sample_handle = NULL;
|
||||
test_handle = NULL;
|
||||
if (g_mode == 2)
|
||||
{
|
||||
MESA_handle_runtime_log_destruction();
|
||||
}
|
||||
printf("%d thread write %d log using %ld us, avg speed %f /s, %ld -> %ld\n", g_thread_num, g_thread_num * g_log_num, g_end_time - g_start_time, ((float)(g_thread_num * g_log_num) / (float)(g_end_time - g_start_time)) * 1000000, g_start_time, g_end_time);
|
||||
exit(0);
|
||||
printf("ctrl+c recviced!\n");
|
||||
MESA_destroy_runtime_log_handle(sample_handle);
|
||||
MESA_destroy_runtime_log_handle(test_handle);
|
||||
sample_handle = NULL;
|
||||
test_handle = NULL;
|
||||
if (g_mode == 2)
|
||||
{
|
||||
MESA_handle_runtime_log_destruction();
|
||||
}
|
||||
printf("%d thread write %d log using %ld us, avg speed %f /s, %ld -> %ld\n", g_thread_num, g_thread_num * g_log_num, g_end_time - g_start_time, ((float)(g_thread_num * g_log_num) / (float)(g_end_time - g_start_time)) * 1000000, g_start_time, g_end_time);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void sig_hup_handler(int sig)
|
||||
@@ -129,26 +147,30 @@ int main(int argc, char ** args)
|
||||
{
|
||||
printf("get log sample_handle error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
test_handle = MESA_create_runtime_log_handle("./log/test_log", RLOG_LV_DEBUG);
|
||||
if(test_handle == NULL)
|
||||
{
|
||||
printf("get log test_handle error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0; i < g_thread_num; i++)
|
||||
{
|
||||
pthread_create(&t[i], NULL, thread_logger, (void *)(unsigned long)(i));
|
||||
}
|
||||
signal(SIGINT, sig_int_handler);
|
||||
signal(SIGHUP, sig_hup_handler);
|
||||
while(1)
|
||||
;
|
||||
//MESA_destroy_runtime_log_handle(sample_handle);
|
||||
//MESA_destroy_runtime_log_handle(test_handle);
|
||||
//sample_handle = NULL;
|
||||
//test_handle = NULL;
|
||||
}
|
||||
if(MESA_handle_fmt_rule_register(sample_handle, 'A', MESA_fmt_handler_A, 512) < 0){
|
||||
printf("struct a register error\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
test_handle = MESA_create_runtime_log_handle("./log/test_log", RLOG_LV_DEBUG);
|
||||
if(test_handle == NULL)
|
||||
{
|
||||
printf("get log test_handle error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0; i < g_thread_num; i++)
|
||||
{
|
||||
pthread_create(&t[i], NULL, thread_logger, (void *)(unsigned long)(i));
|
||||
}
|
||||
//signal(SIGINT, sig_int_handler);
|
||||
//signal(SIGHUP, sig_hup_handler);
|
||||
while(1)
|
||||
;
|
||||
//MESA_destroy_runtime_log_handle(sample_handle);
|
||||
//MESA_destroy_runtime_log_handle(test_handle);
|
||||
//sample_handle = NULL;
|
||||
//test_handle = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
typedef int (*MESA_fmt_handler)(void *info, char *buf, int buflen);
|
||||
int MESA_handle_fmt_rule_register(void *handle, char sign, MESA_fmt_handler handler, int buflen);
|
||||
int MESA_handle_check_fmt_sign(void *handle, char sign, MESA_fmt_handler *handler, char **buf, int *maxlen);
|
||||
|
||||
|
||||
#define RLOG_LV_DEBUG 10
|
||||
#define RLOG_LV_INFO 20
|
||||
#define RLOG_LV_FATAL 30
|
||||
@@ -21,7 +27,7 @@ extern "C"
|
||||
|
||||
int MESA_handle_runtime_log_creation(const char *conf_path);
|
||||
int MESA_handle_runtime_log_reconstruction(const char *conf_path);
|
||||
void MESA_handle_runtime_log_destruction();
|
||||
void MESA_handle_runtime_log_destruction(void);
|
||||
|
||||
#define MESA_HANDLE_RUNTIME_LOG(handle, lv, mod, fmt, args...) \
|
||||
MESA_handle_runtime_log((handle), (lv), (mod), "file %s, line %d, " fmt, \
|
||||
|
||||
104
inc/MESA_shm_ring_queue.h
Normal file
104
inc/MESA_shm_ring_queue.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef _MESA_SHM_RING_QUEUE_H_
|
||||
#define _MESA_SHM_RING_QUEUE_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#define MESA_SHM_LOG_PATH_LEN 1024
|
||||
#define MESA_SHM_RING_QUEUE_NUM 128
|
||||
#define MESA_SHM_RING_QUEUE_BLOCK_NUM 8192
|
||||
#define MESA_SHM_RING_QUEUE_BLOCK_SIZE 4096
|
||||
#define MESA_SHM_KEY_OVERVIEW 35720
|
||||
#define MESA_SHM_KEY_MIN (MESA_SHM_KEY_OVERVIEW + 1)
|
||||
#define MESA_SHM_KEY_MAX (MESA_SHM_KEY_MIN + MESA_SHM_RING_QUEUE_NUM -1)
|
||||
#define MESA_SHM_LOG_BUF_PREFIX_LEN 1024
|
||||
|
||||
|
||||
|
||||
#define MESA_CONSUMER_RUNNING 1
|
||||
#define MESA_CONSUMER_NOT_RUNNING 0
|
||||
#define MESA_SHM_PID_NUM 128
|
||||
|
||||
#define MESA_MATCH_CONSUMER_RULE_SUCCESS 0
|
||||
#define MESA_MATCH_CONSUMER_RULE_FAIL -1
|
||||
#define MESA_MATCH_CONSUMER_RULE_FAIL_STATUS -2
|
||||
#define MESA_MATCH_CONSUMER_RULE_FAIL_LEVEL -3
|
||||
#define MESA_MATCH_CONSUMER_RULE_FAIL_PID -4
|
||||
#define MESA_MATCH_CONSUMER_RULE_FAIL_MODULE -5
|
||||
|
||||
|
||||
#define MESA_SHM_MODULE_NUM 32
|
||||
#define MESA_SHM_MODULE_NAME_LEN 128
|
||||
|
||||
|
||||
#define MESA_CONSUMER_LEVEL_ALL 0
|
||||
#define MESA_CONSUMER_LEVEL_DEBUG (1 << 0)
|
||||
#define MESA_CONSUMER_LEVEL_INFO (1 << 1)
|
||||
#define MESA_CONSUMER_LEVEL_FATAL (1 << 2)
|
||||
|
||||
|
||||
|
||||
|
||||
struct MESA_shm_overview{
|
||||
int shmkey;
|
||||
int shmid;
|
||||
int idx;
|
||||
int producer_pid;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
struct MESA_shm_queue_head{
|
||||
unsigned int blksize;
|
||||
unsigned int blknum;
|
||||
volatile unsigned int rd_idx;
|
||||
volatile unsigned int wr_idx;
|
||||
int ovw_idx;
|
||||
};
|
||||
|
||||
struct MESA_shm_consumer{
|
||||
int status;
|
||||
int log_level;
|
||||
unsigned long long reload_age;
|
||||
int pid[MESA_SHM_PID_NUM];
|
||||
char module[MESA_SHM_MODULE_NUM][sizeof(int) + MESA_SHM_MODULE_NAME_LEN];
|
||||
};
|
||||
|
||||
struct MESA_shm_log_buf{
|
||||
char *buf;
|
||||
char *log_file;
|
||||
char *module;
|
||||
int buflen;
|
||||
int log_file_len;
|
||||
int module_len;
|
||||
int level;
|
||||
};
|
||||
|
||||
int MESA_shm_alloc_overview(struct MESA_shm_overview **ovw, int *ovw_id, struct MESA_shm_consumer **consumer_status);
|
||||
struct MESA_shm_queue_head *MESA_shm_get_ring_queue();
|
||||
void MESA_shm_init();
|
||||
void MESA_shm_recycle_ring_queue(struct MESA_shm_queue_head *ring_queue_head);
|
||||
int MESA_shm_copy_buf_to_ring_queue(struct MESA_shm_queue_head *head, struct MESA_shm_log_buf *lbuf);
|
||||
int MESA_shm_ring_queue_is_empty(struct MESA_shm_queue_head *head);
|
||||
int MESA_shm_ring_queue_is_full(struct MESA_shm_queue_head *head);
|
||||
void *MESA_shm_ring_queue_get_read_pos(struct MESA_shm_queue_head *head);
|
||||
void *MESA_shm_ring_queue_get_write_pos(struct MESA_shm_queue_head *head);
|
||||
void MESA_shm_ring_queue_push_read_pos(struct MESA_shm_queue_head *head);
|
||||
void MESA_shm_ring_queue_push_write_pos(struct MESA_shm_queue_head *head);
|
||||
int MESA_shm_match_consumer_rule(int level, int pid, const char *module);
|
||||
void MESA_shm_unlink(struct MESA_shm_overview *ovw, int ovw_shmid);
|
||||
const char *loglevel_to_name(int level);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
783
inc/list.h
Normal file
783
inc/list.h
Normal file
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
* @file list.h
|
||||
* @author PF
|
||||
* @date 2017/05/1
|
||||
*
|
||||
* port from linux kernel list.h: https://github.com/torvalds/linux/raw/master/include/linux/list.h
|
||||
*
|
||||
* Here is a recipe to cook list.h for user space program.
|
||||
* 1. copy list.h from linux/include/list.h
|
||||
* 2. remove
|
||||
* - #ifdef __KERNE__ and its #endif
|
||||
* - all #include line
|
||||
* - prefetch() and rcu related functions
|
||||
* 3. add macro offsetof() and container_of
|
||||
*/
|
||||
|
||||
#ifndef LIST_H_
|
||||
#define LIST_H_ (1)
|
||||
|
||||
// import from include/linux/types.h
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
struct hlist_head {
|
||||
struct hlist_node *first;
|
||||
};
|
||||
|
||||
struct hlist_node {
|
||||
struct hlist_node *next, **pprev;
|
||||
};
|
||||
|
||||
// import from include/linux/poison.h
|
||||
|
||||
/*
|
||||
* Architectures might want to move the poison pointer offset
|
||||
* into some well-recognized area such as 0xdead000000000000,
|
||||
* that is also not mappable by user-space exploits:
|
||||
*/
|
||||
#ifdef CONFIG_ILLEGAL_POINTER_VALUE
|
||||
# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
|
||||
#else
|
||||
# define POISON_POINTER_DELTA (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These are non-NULL pointers that will result in page faults
|
||||
* under normal circumstances, used to verify that nobody uses
|
||||
* non-initialized list entries.
|
||||
*/
|
||||
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
|
||||
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
|
||||
|
||||
// import from include/linux/stddef.h
|
||||
#undef offsetof
|
||||
#ifdef __compiler_offsetof
|
||||
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
|
||||
#else
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
// import from include/linux/kernel.h
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
/*
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list) {
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
#ifndef CONFIG_DEBUG_LIST
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next) {
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
#else
|
||||
extern void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head) {
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head) {
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head *prev, struct list_head *next) {
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty() on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
#ifndef CONFIG_DEBUG_LIST
|
||||
|
||||
static inline void __list_del_entry(struct list_head *entry) {
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *entry) {
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = LIST_POISON1;
|
||||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
#else
|
||||
extern void __list_del_entry(struct list_head *entry);
|
||||
extern void list_del(struct list_head *entry);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* list_replace - replace old entry by new one
|
||||
* @old : the element to be replaced
|
||||
* @new : the new element to insert
|
||||
*
|
||||
* If @old was empty, it will be overwritten.
|
||||
*/
|
||||
static inline void list_replace(struct list_head *old,
|
||||
struct list_head *new) {
|
||||
new->next = old->next;
|
||||
new->next->prev = new;
|
||||
new->prev = old->prev;
|
||||
new->prev->next = new;
|
||||
}
|
||||
|
||||
static inline void list_replace_init(struct list_head *old,
|
||||
struct list_head *new) {
|
||||
list_replace(old, new);
|
||||
INIT_LIST_HEAD(old);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static inline void list_del_init(struct list_head *entry) {
|
||||
__list_del_entry(entry);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move - delete from one list and add as another's head
|
||||
* @list: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void list_move(struct list_head *list, struct list_head *head) {
|
||||
__list_del_entry(list);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move_tail - delete from one list and add as another's tail
|
||||
* @list: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void list_move_tail(struct list_head *list,
|
||||
struct list_head *head) {
|
||||
__list_del_entry(list);
|
||||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_last - tests whether @list is the last entry in list @head
|
||||
* @list: the entry to test
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline int list_is_last(const struct list_head *list,
|
||||
const struct list_head *head) {
|
||||
return list->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_empty(const struct list_head *head) {
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty_careful - tests whether a list is empty and not being modified
|
||||
* @head: the list to test
|
||||
*
|
||||
* Description:
|
||||
* tests whether a list is empty _and_ checks that no other CPU might be
|
||||
* in the process of modifying either member (next or prev)
|
||||
*
|
||||
* NOTE: using list_empty_careful() without synchronization
|
||||
* can only be safe if the only activity that can happen
|
||||
* to the list entry is list_del_init(). Eg. it cannot be used
|
||||
* if another CPU could re-list_add() it.
|
||||
*/
|
||||
static inline int list_empty_careful(const struct list_head *head) {
|
||||
struct list_head *next = head->next;
|
||||
return (next == head) && (next == head->prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_rotate_left - rotate the list to the left
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline void list_rotate_left(struct list_head *head) {
|
||||
struct list_head *first;
|
||||
|
||||
if (!list_empty(head)) {
|
||||
first = head->next;
|
||||
list_move_tail(first, head);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_singular - tests whether a list has just one entry.
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_is_singular(const struct list_head *head) {
|
||||
return !list_empty(head) && (head->next == head->prev);
|
||||
}
|
||||
|
||||
static inline void __list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry) {
|
||||
struct list_head *new_first = entry->next;
|
||||
list->next = head->next;
|
||||
list->next->prev = list;
|
||||
list->prev = entry;
|
||||
entry->next = list;
|
||||
head->next = new_first;
|
||||
new_first->prev = head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_cut_position - cut a list into two
|
||||
* @list: a new list to add all removed entries
|
||||
* @head: a list with entries
|
||||
* @entry: an entry within head, could be the head itself
|
||||
* and if so we won't cut the list
|
||||
*
|
||||
* This helper moves the initial part of @head, up to and
|
||||
* including @entry, from @head to @list. You should
|
||||
* pass on @entry an element you know is on @head. @list
|
||||
* should be an empty list or a list you do not care about
|
||||
* losing its data.
|
||||
*
|
||||
*/
|
||||
static inline void list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry) {
|
||||
if (list_empty(head)) {
|
||||
return;
|
||||
}
|
||||
if (list_is_singular(head) &&
|
||||
(head->next != entry && head != entry)) {
|
||||
return;
|
||||
}
|
||||
if (entry == head) {
|
||||
INIT_LIST_HEAD(list);
|
||||
} else {
|
||||
__list_cut_position(list, head, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __list_splice(const struct list_head *list,
|
||||
struct list_head *prev,
|
||||
struct list_head *next) {
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
|
||||
first->prev = prev;
|
||||
prev->next = first;
|
||||
|
||||
last->next = next;
|
||||
next->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists, this is designed for stacks
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice(const struct list_head *list,
|
||||
struct list_head *head) {
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head, head->next);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail - join two lists, each list being a queue
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice_tail(struct list_head *list,
|
||||
struct list_head *head) {
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head->prev, head);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_init(struct list_head *list,
|
||||
struct list_head *head) {
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head, head->next);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail_init - join two lists and reinitialise the emptied list
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* Each of the lists is a queue.
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_tail_init(struct list_head *list,
|
||||
struct list_head *head) {
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head->prev, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* list_first_entry - get the first element from a list
|
||||
* @ptr: the list head to take the element from.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Note, that list is expected to be not empty.
|
||||
*/
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
/**
|
||||
* list_last_entry - get the last element from a list
|
||||
* @ptr: the list head to take the element from.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Note, that list is expected to be not empty.
|
||||
*/
|
||||
#define list_last_entry(ptr, type, member) \
|
||||
list_entry((ptr)->prev, type, member)
|
||||
|
||||
/**
|
||||
* list_first_entry_or_null - get the first element from a list
|
||||
* @ptr: the list head to take the element from.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Note that if the list is empty, it returns NULL.
|
||||
*/
|
||||
#define list_first_entry_or_null(ptr, type, member) \
|
||||
(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
|
||||
|
||||
/**
|
||||
* list_next_entry - get the next element in list
|
||||
* @pos: the type * to cursor
|
||||
* @member: the name of the list_head within the struct.
|
||||
*/
|
||||
#define list_next_entry(pos, member) \
|
||||
list_entry((pos)->member.next, typeof(*(pos)), member)
|
||||
|
||||
/**
|
||||
* list_prev_entry - get the prev element in list
|
||||
* @pos: the type * to cursor
|
||||
* @member: the name of the list_head within the struct.
|
||||
*/
|
||||
#define list_prev_entry(pos, member) \
|
||||
list_entry((pos)->member.prev, typeof(*(pos)), member)
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev - iterate over a list backwards
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev; pos != (head); pos = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev_safe(pos, n, head) \
|
||||
for (pos = (head)->prev, n = pos->prev; \
|
||||
pos != (head); \
|
||||
pos = n, n = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*/
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_first_entry(head, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_next_entry(pos, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = list_last_entry(head, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_prev_entry(pos, member))
|
||||
|
||||
/**
|
||||
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
|
||||
* @pos: the type * to use as a start point
|
||||
* @head: the head of the list
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
|
||||
*/
|
||||
#define list_prepare_entry(pos, head, member) \
|
||||
((pos) ? : list_entry(head, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue - continue iteration over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Continue to iterate over list of given type, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = list_next_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_next_entry(pos, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue_reverse - iterate backwards from the given point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Start to iterate over list of given type backwards, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue_reverse(pos, head, member) \
|
||||
for (pos = list_prev_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_prev_entry(pos, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_from - iterate over list of given type from the current point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing from current position.
|
||||
*/
|
||||
#define list_for_each_entry_from(pos, head, member) \
|
||||
for (; &pos->member != (head); \
|
||||
pos = list_next_entry(pos, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_first_entry(head, typeof(*pos), member), \
|
||||
n = list_next_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_next_entry(n, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_continue - continue list iteration safe against removal
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing after current point,
|
||||
* safe against removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
||||
for (pos = list_next_entry(pos, member), \
|
||||
n = list_next_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_next_entry(n, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Iterate over list of given type from current point, safe against
|
||||
* removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_from(pos, n, head, member) \
|
||||
for (n = list_next_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_next_entry(n, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* Iterate backwards over list of given type, safe against removal
|
||||
* of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
||||
for (pos = list_last_entry(head, typeof(*pos), member), \
|
||||
n = list_prev_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_prev_entry(n, member))
|
||||
|
||||
/**
|
||||
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
|
||||
* @pos: the loop cursor used in the list_for_each_entry_safe loop
|
||||
* @n: temporary storage used in list_for_each_entry_safe
|
||||
* @member: the name of the list_head within the struct.
|
||||
*
|
||||
* list_safe_reset_next is not safe to use in general if the list may be
|
||||
* modified concurrently (eg. the lock is dropped in the loop body). An
|
||||
* exception to this is if the cursor element (pos) is pinned in the list,
|
||||
* and list_safe_reset_next is called after re-taking the lock and before
|
||||
* completing the current iteration of the loop body.
|
||||
*/
|
||||
#define list_safe_reset_next(pos, n, member) \
|
||||
n = list_next_entry(pos, member)
|
||||
|
||||
/*
|
||||
* Double linked lists with a single pointer list head.
|
||||
* Mostly useful for hash tables where the two pointer list head is
|
||||
* too wasteful.
|
||||
* You lose the ability to access the tail in O(1).
|
||||
*/
|
||||
|
||||
#define HLIST_HEAD_INIT { .first = NULL }
|
||||
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
|
||||
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
|
||||
|
||||
static inline void INIT_HLIST_NODE(struct hlist_node *h) {
|
||||
h->next = NULL;
|
||||
h->pprev = NULL;
|
||||
}
|
||||
|
||||
static inline int hlist_unhashed(const struct hlist_node *h) {
|
||||
return !h->pprev;
|
||||
}
|
||||
|
||||
static inline int hlist_empty(const struct hlist_head *h) {
|
||||
return !h->first;
|
||||
}
|
||||
|
||||
static inline void __hlist_del(struct hlist_node *n) {
|
||||
struct hlist_node *next = n->next;
|
||||
struct hlist_node **pprev = n->pprev;
|
||||
*pprev = next;
|
||||
if (next)
|
||||
next->pprev = pprev;
|
||||
}
|
||||
|
||||
static inline void hlist_del(struct hlist_node *n) {
|
||||
__hlist_del(n);
|
||||
n->next = LIST_POISON1;
|
||||
n->pprev = LIST_POISON2;
|
||||
}
|
||||
|
||||
static inline void hlist_del_init(struct hlist_node *n) {
|
||||
if (!hlist_unhashed(n)) {
|
||||
__hlist_del(n);
|
||||
INIT_HLIST_NODE(n);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) {
|
||||
struct hlist_node *first = h->first;
|
||||
n->next = first;
|
||||
if (first) {
|
||||
first->pprev = &n->next;
|
||||
}
|
||||
h->first = n;
|
||||
n->pprev = &h->first;
|
||||
}
|
||||
|
||||
/* next must be != NULL */
|
||||
static inline void hlist_add_before(struct hlist_node *n,
|
||||
struct hlist_node *next) {
|
||||
n->pprev = next->pprev;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
*(n->pprev) = n;
|
||||
}
|
||||
|
||||
static inline void hlist_add_behind(struct hlist_node *n,
|
||||
struct hlist_node *prev) {
|
||||
n->next = prev->next;
|
||||
prev->next = n;
|
||||
n->pprev = &prev->next;
|
||||
|
||||
if (n->next) {
|
||||
n->next->pprev = &n->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* after that we'll appear to be on some hlist and hlist_del will work */
|
||||
static inline void hlist_add_fake(struct hlist_node *n) {
|
||||
n->pprev = &n->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move a list from one list head to another. Fixup the pprev
|
||||
* reference of the first entry if it exists.
|
||||
*/
|
||||
static inline void hlist_move_list(struct hlist_head *old,
|
||||
struct hlist_head *new) {
|
||||
new->first = old->first;
|
||||
if (new->first) {
|
||||
new->first->pprev = &new->first;
|
||||
}
|
||||
old->first = NULL;
|
||||
}
|
||||
|
||||
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
|
||||
|
||||
#define hlist_for_each(pos, head) \
|
||||
for (pos = (head)->first; pos ; pos = pos->next)
|
||||
|
||||
#define hlist_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
|
||||
pos = n)
|
||||
|
||||
#define hlist_entry_safe(ptr, type, member) \
|
||||
({ typeof(ptr) ____ptr = (ptr); \
|
||||
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
|
||||
})
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry(pos, head, member) \
|
||||
for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
|
||||
pos; \
|
||||
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_continue(pos, member) \
|
||||
for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
|
||||
pos; \
|
||||
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_from(pos, member) \
|
||||
for (; pos; \
|
||||
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another &struct hlist_node to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
|
||||
pos && ({ n = pos->member.next; 1; }); \
|
||||
pos = hlist_entry_safe(n, typeof(*pos), member))
|
||||
|
||||
#endif // LIST_H_
|
||||
24
inc/snprintf.h
Normal file
24
inc/snprintf.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef _PORTABLE_SNPRINTF_H_
|
||||
#define _PORTABLE_SNPRINTF_H_
|
||||
|
||||
#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
|
||||
#define PORTABLE_SNPRINTF_VERSION_MINOR 2
|
||||
|
||||
#ifdef HAVE_SNPRINTF
|
||||
#include <stdio.h>
|
||||
#else
|
||||
extern int snprintf(char *, size_t, const char *, /*args*/ ...);
|
||||
extern int vsnprintf(char *, size_t, const char *, va_list);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)
|
||||
extern int portable_snprintf(void * handle, char *str, size_t str_m, const char *fmt, /*args*/ ...);
|
||||
extern int portable_vsnprintf(void *handle, char *str, size_t str_m, const char *fmt, va_list ap);
|
||||
/*#define snprintf portable_snprintf*/ /*others use libc snprintf*/
|
||||
/*#define vsnprintf portable_vsnprintf*/
|
||||
#endif
|
||||
extern int asprintf (void * handle, char **ptr, const char *fmt, /*args*/ ...);
|
||||
extern int vasprintf (void * handle, char **ptr, const char *fmt, va_list ap);
|
||||
extern int asnprintf (void * handle, char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
|
||||
extern int vasnprintf(void * handle, char **ptr, size_t str_m, const char *fmt, va_list ap);
|
||||
#endif
|
||||
875
shm_consumer/MESA_shm_consumer.c
Normal file
875
shm_consumer/MESA_shm_consumer.c
Normal file
@@ -0,0 +1,875 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <limits.h>
|
||||
#include "list.h"
|
||||
#include "MESA_handle_logger.h"
|
||||
#include "MESA_shm_ring_queue.h"
|
||||
|
||||
#define DEFAUT_BUF_SIZE 256
|
||||
#define DEFAULT_COMMAND_LEN 1024
|
||||
#define CONSUMER_SUCCESS 0
|
||||
#define CONSUMER_ERROR -1
|
||||
|
||||
#define CONSUMER_OUTPUT_MODE_FILE 0
|
||||
#define CONSUMER_OUTPUT_MODE_TERMINAL 1
|
||||
#define CONSUMER_PID_MAX_LEN 128
|
||||
|
||||
#ifdef CONSUMER_DEBUG
|
||||
#define consumer_debug(...) \
|
||||
do{ \
|
||||
FILE *fp = fopen("/var/log/MESA_consumer_debug", "a+"); \
|
||||
if(fp != NULL){ \
|
||||
fprintf(fp, __VA_ARGS__); \
|
||||
fclose(fp); \
|
||||
} \
|
||||
}while(0)
|
||||
#else
|
||||
#define consumer_debug(...)
|
||||
#endif
|
||||
|
||||
|
||||
struct log_file_list{
|
||||
char log_file_pre[MESA_SHM_LOG_PATH_LEN];
|
||||
char real_log_file[MESA_SHM_LOG_PATH_LEN];
|
||||
struct tm create_date;
|
||||
int fd;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
struct list_head list;
|
||||
};
|
||||
struct pid_list{
|
||||
int pid;
|
||||
struct list_head list;
|
||||
};
|
||||
struct module_list{
|
||||
char module[MESA_SHM_MODULE_NAME_LEN];
|
||||
struct list_head list;
|
||||
};
|
||||
struct log_file_list g_log_file_list;
|
||||
struct pid_list g_pid_list;
|
||||
struct module_list g_module_list;
|
||||
int g_output_mode = CONSUMER_OUTPUT_MODE_FILE;
|
||||
struct MESA_shm_consumer *g_status = NULL;
|
||||
int g_tmp_lv = MESA_CONSUMER_LEVEL_ALL;
|
||||
int g_cur_tty_fd = -1;
|
||||
struct MESA_shm_overview *g_shm_overview = NULL;
|
||||
int g_shm_overview_id = -1;
|
||||
|
||||
|
||||
void init_ring_queue_head_arrray(struct MESA_shm_overview *ovw, struct MESA_shm_queue_head **ring_queue_head)
|
||||
{
|
||||
int i = 0;
|
||||
struct MESA_shm_overview *tmp_ovw = NULL;
|
||||
for(i = 0; i< MESA_SHM_RING_QUEUE_NUM; i++){
|
||||
tmp_ovw = ovw + i;
|
||||
if(tmp_ovw->shmid == -1){
|
||||
break;
|
||||
}
|
||||
ring_queue_head[i] = shmat(tmp_ovw->shmid, NULL, 0);
|
||||
if(ring_queue_head[i] == (struct MESA_shm_queue_head *)-1){
|
||||
ring_queue_head[i] = NULL;
|
||||
}
|
||||
}
|
||||
return ;
|
||||
}
|
||||
void consumer_daemo()
|
||||
{
|
||||
int fd = -1;
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
printf("fork error\n");
|
||||
return ;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
if (setsid() == -1) {
|
||||
return ;
|
||||
}
|
||||
umask(0);
|
||||
fd = open("/dev/null", O_RDWR);
|
||||
if (fd == -1) {
|
||||
return ;
|
||||
}
|
||||
if (dup2(fd, STDIN_FILENO) == -1) {
|
||||
return ;
|
||||
}
|
||||
|
||||
if (dup2(fd, STDOUT_FILENO) == -1) {
|
||||
return ;
|
||||
}
|
||||
if (fd > STDERR_FILENO) {
|
||||
if (close(fd) == -1) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
return ;
|
||||
}
|
||||
int consumer_is_running(char *process_name)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char buf[DEFAUT_BUF_SIZE] = {0};
|
||||
int count = 0;
|
||||
char command[DEFAUT_BUF_SIZE] = {0};
|
||||
if(process_name == NULL){
|
||||
return 0;
|
||||
}
|
||||
snprintf(command, sizeof(command), "ps -ef | grep %s | grep -v grep | wc -l", process_name);
|
||||
fp = popen(command, "r");
|
||||
if(fp == NULL){
|
||||
return 0;
|
||||
}
|
||||
if((fgets(buf, sizeof(buf), fp)) != NULL){
|
||||
count = atoi(buf);
|
||||
}
|
||||
pclose(fp);
|
||||
if(count > 1){
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char *get_exe_name(char *argv)
|
||||
{
|
||||
char * p = NULL;
|
||||
p = rindex(argv, '/');
|
||||
if(p == NULL){
|
||||
p = argv;
|
||||
}else{
|
||||
p = p + 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
struct log_file_list *get_log_file_node(char *log_file)
|
||||
{
|
||||
struct log_file_list *tmp;
|
||||
struct log_file_list *n;
|
||||
list_for_each_entry_safe(tmp, n, &g_log_file_list.list, list){
|
||||
if(strcmp(tmp->log_file_pre, log_file) == 0){
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct log_file_list * create_log_file_node(char *log_file)
|
||||
{
|
||||
struct log_file_list *node;
|
||||
struct tm date;
|
||||
time_t curtime = 0;
|
||||
struct stat buf;
|
||||
int n = 0;
|
||||
node = (struct log_file_list *)malloc(sizeof(struct log_file_list));
|
||||
if(node == NULL){
|
||||
return NULL;
|
||||
}
|
||||
memset(node, 0 ,sizeof(struct log_file_list));
|
||||
memcpy(node->log_file_pre, log_file, strlen(log_file));
|
||||
curtime = time(NULL);
|
||||
localtime_r(&curtime, &date);
|
||||
n = snprintf(node->real_log_file, sizeof(node->real_log_file), "%s.%d-%d-%d",
|
||||
node->log_file_pre, date.tm_year + 1900, date.tm_mon + 1, date.tm_mday);
|
||||
if(n >= sizeof(node->real_log_file)){
|
||||
consumer_debug("log file name %s is too long, func = %s, line = %d\n", node->real_log_file, __FUNCTION__, __LINE__);
|
||||
goto error;
|
||||
}
|
||||
node->fd = open(node->real_log_file, O_RDWR | O_CREAT | O_APPEND, 0666);
|
||||
if(node->fd < 0){
|
||||
consumer_debug("open file %s failed, func = %s, line = %d\n", node->real_log_file, __FUNCTION__, __LINE__);
|
||||
goto error;
|
||||
}
|
||||
if(stat(node->real_log_file, &buf) < 0){
|
||||
consumer_debug("stat file %s failed, func = %s, line = %d\n", node->real_log_file, __FUNCTION__, __LINE__);
|
||||
goto error;
|
||||
}
|
||||
node->dev = buf.st_dev;
|
||||
node->ino = buf.st_ino;
|
||||
node->create_date = date;
|
||||
list_add(&node->list, &g_log_file_list.list);
|
||||
return node;
|
||||
error:
|
||||
if(node != NULL){
|
||||
free(node);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
void get_cur_date(struct tm *date)
|
||||
{
|
||||
time_t curtime = 0;
|
||||
curtime = time(NULL);
|
||||
localtime_r(&curtime, date);
|
||||
return ;
|
||||
|
||||
}
|
||||
void get_cur_strftime(char *buf, int maxlen)
|
||||
{
|
||||
struct tm local;
|
||||
time_t curtime = time(NULL);
|
||||
localtime_r(&curtime, &local);
|
||||
strftime(buf, maxlen, "%c", &local);
|
||||
return ;
|
||||
}
|
||||
|
||||
int reopen_log_file(struct log_file_list *node)
|
||||
{
|
||||
struct stat buf;
|
||||
close(node->fd);
|
||||
node->fd = open(node->real_log_file, O_RDWR | O_CREAT | O_APPEND, 0666);
|
||||
if(node->fd < 0){
|
||||
consumer_debug("open file %s failed, func = %s, line = %d\n", node->real_log_file, __FUNCTION__, __LINE__);
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(stat(node->real_log_file, &buf) < 0){
|
||||
consumer_debug("stat file %s failed, func = %s, line = %d\n", node->real_log_file, __FUNCTION__, __LINE__);
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
node->dev = buf.st_dev;
|
||||
node->ino = buf.st_ino;
|
||||
return CONSUMER_SUCCESS;
|
||||
|
||||
}
|
||||
int check_reopen_log_file(struct log_file_list *node)
|
||||
{
|
||||
struct stat buf;
|
||||
struct tm date;
|
||||
int n = 0;
|
||||
get_cur_date(&date);
|
||||
if(date.tm_year != node->create_date.tm_year
|
||||
|| date.tm_mon != node->create_date.tm_mon
|
||||
|| date.tm_mday != node->create_date.tm_mday){
|
||||
|
||||
node->create_date = date;
|
||||
n = snprintf(node->real_log_file, sizeof(node->real_log_file), "%s.%d-%d-%d", node->log_file_pre,
|
||||
node->create_date.tm_year + 1900, node->create_date.tm_mon + 1, node->create_date.tm_mday);
|
||||
if(n >= sizeof(node->real_log_file)){
|
||||
consumer_debug("log file name %s is too long, func = %s, line = %d\n", node->real_log_file, __FUNCTION__, __LINE__);
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
return reopen_log_file(node);
|
||||
}
|
||||
if(stat(node->real_log_file, &buf) < 0){
|
||||
return reopen_log_file(node); /* we'll have to restat the newly created file to get the inode info*/
|
||||
}
|
||||
if(buf.st_dev != node->dev || buf.st_ino != node->ino){
|
||||
return reopen_log_file(node);
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
(int)file_len + (str)file + '\0' + (int)level + (int)module_len + (str)module + '\0' + (int)payload_len + (str)payload + '\0'
|
||||
*/
|
||||
void consumer_ring_queue_to_file(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
int *p_file_len = NULL, *p_payload_len = NULL, *p_level = NULL, *p_module_len = NULL;
|
||||
char *p_file = NULL, *payload = NULL, *p_module = NULL;
|
||||
char buf[MESA_SHM_LOG_BUF_PREFIX_LEN + MESA_SHM_RING_QUEUE_BLOCK_SIZE] = {0};
|
||||
int n = 0;
|
||||
char strtime[DEFAUT_BUF_SIZE] = {0};
|
||||
struct log_file_list *node = NULL;
|
||||
get_cur_strftime(strtime, sizeof(strtime));
|
||||
while(!MESA_shm_ring_queue_is_empty(head)){
|
||||
p_file_len = (int *)MESA_shm_ring_queue_get_read_pos(head);
|
||||
p_file = (char *)(p_file_len + 1);
|
||||
node = get_log_file_node(p_file);
|
||||
if(node == NULL){
|
||||
node = create_log_file_node(p_file);
|
||||
if(node == NULL){
|
||||
MESA_shm_ring_queue_push_read_pos(head);
|
||||
continue ;
|
||||
}
|
||||
}else{
|
||||
if(check_reopen_log_file(node) != CONSUMER_SUCCESS){
|
||||
MESA_shm_ring_queue_push_read_pos(head);
|
||||
continue ;
|
||||
}
|
||||
}
|
||||
p_level = (int *)(p_file + *p_file_len + 1);
|
||||
p_module_len = p_level + 1;
|
||||
p_module = (char *)(p_module_len + 1);
|
||||
p_payload_len = (int *)(p_module + *p_module_len + 1);
|
||||
payload = (char *)(p_payload_len + 1);
|
||||
n = snprintf(buf, sizeof(buf), "%s, %s, %s, %s\n", strtime, loglevel_to_name(*p_level), p_module, payload);
|
||||
write(node->fd, buf, n);
|
||||
MESA_shm_ring_queue_push_read_pos(head);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
void consumer_ring_queue_to_terminal(struct MESA_shm_queue_head *head, int producer_pid)
|
||||
{
|
||||
int *p_file_len = NULL, *p_payload_len = NULL, *p_level = NULL, *p_module_len = NULL;
|
||||
char *p_file = NULL, *payload = NULL, *p_module = NULL;
|
||||
char buf[MESA_SHM_LOG_BUF_PREFIX_LEN + MESA_SHM_RING_QUEUE_BLOCK_SIZE] = {0};
|
||||
int n = 0;
|
||||
while(!MESA_shm_ring_queue_is_empty(head)){
|
||||
p_file_len = (int *)MESA_shm_ring_queue_get_read_pos(head);
|
||||
p_file = (char *)(p_file_len + 1);
|
||||
p_level = (int *)(p_file + *p_file_len + 1);
|
||||
p_module_len = p_level + 1;
|
||||
p_module = (char *)(p_module_len + 1);
|
||||
p_payload_len = (int *)(p_module + *p_module_len + 1);
|
||||
payload = (char *)(p_payload_len + 1);
|
||||
n = snprintf(buf, sizeof(buf), "pid:%d, %s, %s, %s\n", producer_pid, loglevel_to_name(*p_level), p_module, payload);
|
||||
if(g_cur_tty_fd >= 0){
|
||||
write(g_cur_tty_fd, buf, n);
|
||||
}
|
||||
MESA_shm_ring_queue_push_read_pos(head);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
int pid_is_exist(int producer_pid)
|
||||
{
|
||||
struct pid_list *tmp;
|
||||
struct pid_list *n;
|
||||
list_for_each_entry_safe(tmp, n, &g_pid_list.list, list){
|
||||
if(tmp->pid == producer_pid){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
struct pid_list *create_pid_node(int pid)
|
||||
{
|
||||
struct pid_list *node;
|
||||
node = (struct pid_list *)malloc(sizeof(struct pid_list));
|
||||
if(node == NULL){
|
||||
printf("malloc pid node error\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(node, 0 ,sizeof(struct pid_list));
|
||||
node->pid = pid;
|
||||
return node;
|
||||
}
|
||||
int add_pid_node(struct pid_list *node)
|
||||
{
|
||||
if(!pid_is_exist(node->pid)){
|
||||
list_add(&node->list, &g_pid_list.list);
|
||||
}else{
|
||||
free(node);
|
||||
printf("pid repeat\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int move_pid_list_to_shm(int *pid)
|
||||
{
|
||||
struct pid_list *tmp;
|
||||
struct pid_list *n;
|
||||
int pid_num = 0;
|
||||
list_for_each_entry_safe(tmp, n, &g_pid_list.list, list){
|
||||
if(pid_num < MESA_SHM_PID_NUM){
|
||||
pid[pid_num] = tmp->pid;
|
||||
pid_num ++;
|
||||
}
|
||||
list_del(&tmp->list);
|
||||
free(tmp);
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
int parse_pid(char *p_pid)
|
||||
{
|
||||
char *cur;
|
||||
char *p;
|
||||
char tmp_pid_str[CONSUMER_PID_MAX_LEN] = {0};
|
||||
int tmp_pid;
|
||||
int argcc = 0;
|
||||
struct pid_list *node = NULL;
|
||||
cur = p_pid;
|
||||
p = p_pid;
|
||||
int pid_num = 0;
|
||||
while(*p){
|
||||
if(*p == '\0'){
|
||||
break;
|
||||
}
|
||||
if((*p < '0' || *p > '9') && !isspace(*p)){
|
||||
printf("invalid pid\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while(isspace(*cur)){
|
||||
cur++;
|
||||
}
|
||||
while(1){
|
||||
if(*cur == '\0'){
|
||||
break ;
|
||||
}
|
||||
|
||||
if(!isspace(*cur)){
|
||||
tmp_pid_str[argcc] = *cur;
|
||||
argcc++;
|
||||
cur++;
|
||||
}else{
|
||||
while(isspace(*cur)){
|
||||
cur++;
|
||||
}
|
||||
if(pid_num >= MESA_SHM_PID_NUM){
|
||||
printf("pid num overload\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
pid_num ++;
|
||||
tmp_pid = atoi(tmp_pid_str);
|
||||
node = create_pid_node(tmp_pid);
|
||||
if(node == NULL){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(add_pid_node(node) == CONSUMER_ERROR){
|
||||
printf("add pid error\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
memset(tmp_pid_str, 0 ,sizeof(tmp_pid_str));
|
||||
argcc = 0;
|
||||
}
|
||||
if(argcc >= sizeof(tmp_pid_str)){
|
||||
printf("invalid pid\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(*cur == 0 && tmp_pid_str[0] != '\0'){
|
||||
if(pid_num >= MESA_SHM_PID_NUM){
|
||||
printf("pid num overload\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
tmp_pid = atoi(tmp_pid_str);
|
||||
node = create_pid_node(tmp_pid);
|
||||
if(node == NULL){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(add_pid_node(node) == CONSUMER_ERROR){
|
||||
printf("add pid error\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
struct module_list *create_module_node(char *module)
|
||||
{
|
||||
struct module_list *node;
|
||||
node = (struct module_list *)malloc(sizeof(struct module_list));
|
||||
if(node == NULL){
|
||||
printf("malloc module node error\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(node, 0, sizeof(struct module_list));
|
||||
memcpy(node->module, module, strlen(module));
|
||||
return node;
|
||||
}
|
||||
int module_is_exist(char *module)
|
||||
{
|
||||
struct module_list *tmp;
|
||||
struct module_list *n;
|
||||
list_for_each_entry_safe(tmp, n, &g_module_list.list, list){
|
||||
if(strcmp(tmp->module, module) == 0){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int add_module_node(struct module_list *node)
|
||||
{
|
||||
if(!module_is_exist(node->module)){
|
||||
list_add(&node->list, &g_module_list.list);
|
||||
}else{
|
||||
free(node);
|
||||
printf("module repeat\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
int move_module_list_to_shm(char module[MESA_SHM_MODULE_NUM][sizeof(int) + MESA_SHM_MODULE_NAME_LEN])
|
||||
{
|
||||
struct module_list *tmp;
|
||||
struct module_list *n;
|
||||
int module_num = 0;
|
||||
int module_len = 0;
|
||||
list_for_each_entry_safe(tmp, n, &g_module_list.list, list){
|
||||
if(module_num < MESA_SHM_MODULE_NUM){
|
||||
module_len = strlen(tmp->module);
|
||||
if(module_len < MESA_SHM_MODULE_NAME_LEN){
|
||||
*((int *)module[module_num]) = module_len;
|
||||
memcpy(module[module_num] + sizeof(int), tmp->module, module_len);
|
||||
module_num ++ ;
|
||||
}
|
||||
}
|
||||
list_del(&tmp->list);
|
||||
free(tmp);
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
int parse_module(char *module)
|
||||
{
|
||||
char tmp_module[MESA_SHM_MODULE_NAME_LEN] = {0};
|
||||
int argcc = 0;
|
||||
char *cur = module;
|
||||
int module_num = 0;
|
||||
struct module_list *node = NULL;
|
||||
while(isspace(*cur)){
|
||||
cur++;
|
||||
}
|
||||
while(1){
|
||||
if(*cur == '\0'){
|
||||
break ;
|
||||
}
|
||||
if(!isspace(*cur)){
|
||||
tmp_module[argcc] = *cur;
|
||||
argcc++;
|
||||
cur++;
|
||||
}else{
|
||||
while(isspace(*cur)){
|
||||
cur++;
|
||||
}
|
||||
if(module_num >= MESA_SHM_MODULE_NUM){
|
||||
printf("module num overload\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
module_num ++;
|
||||
node = create_module_node(tmp_module);
|
||||
if(node == NULL){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(add_module_node(node) == CONSUMER_ERROR){
|
||||
printf("add module error\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
memset(tmp_module, 0 ,sizeof(tmp_module));
|
||||
argcc = 0;
|
||||
}
|
||||
if(argcc >= sizeof(tmp_module)){
|
||||
printf("invalid module\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(*cur == 0 && tmp_module[0] != '\0'){
|
||||
if(module_num >= MESA_SHM_MODULE_NUM){
|
||||
printf("module num overload\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
node = create_module_node(tmp_module);
|
||||
if(node == NULL){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(add_module_node(node) == CONSUMER_ERROR){
|
||||
printf("add module error\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int check_level(char *level)
|
||||
{
|
||||
if(level == NULL || level[0] == '\0'){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(strcasecmp(level, "all") == 0){
|
||||
g_tmp_lv = MESA_CONSUMER_LEVEL_ALL;
|
||||
}else if(strcasecmp(level, "debug") == 0){
|
||||
g_tmp_lv = g_tmp_lv | MESA_CONSUMER_LEVEL_DEBUG;
|
||||
}else if(strcasecmp(level, "info") == 0){
|
||||
g_tmp_lv = g_tmp_lv | MESA_CONSUMER_LEVEL_INFO;
|
||||
}else if(strcasecmp(level, "fatal") == 0){
|
||||
g_tmp_lv = g_tmp_lv | MESA_CONSUMER_LEVEL_FATAL;
|
||||
}else{
|
||||
printf("invalid log level\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
int parse_log_level(char *level)
|
||||
{
|
||||
char tmp_level[DEFAUT_BUF_SIZE] = {0};
|
||||
int argcc = 0;
|
||||
char *cur = level;
|
||||
while(isspace(*cur)){
|
||||
cur++;
|
||||
}
|
||||
while(1){
|
||||
if(*cur == '\0'){
|
||||
break ;
|
||||
}
|
||||
if(!isspace(*cur)){
|
||||
tmp_level[argcc] = *cur;
|
||||
argcc++;
|
||||
cur++;
|
||||
}else{
|
||||
while(isspace(*cur)){
|
||||
cur++;
|
||||
}
|
||||
if(check_level(tmp_level) == CONSUMER_ERROR){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
memset(tmp_level, 0 ,sizeof(tmp_level));
|
||||
argcc = 0;
|
||||
}
|
||||
if(argcc >= sizeof(tmp_level)){
|
||||
printf("invalid level\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(*cur == 0 && tmp_level[0] != '\0'){
|
||||
if(check_level(tmp_level) == CONSUMER_ERROR){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
|
||||
int get_options(int argc, char **argv)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
for(i = 1; i < argc; i++){
|
||||
p = argv[i];
|
||||
if(*p++ != '-'){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
if(strcmp(p, "pid") == 0){
|
||||
if(argv[++i]){
|
||||
if(strcmp(argv[i], "all") == 0){
|
||||
/*do nothing now*/
|
||||
}else{
|
||||
if(parse_pid(argv[i]) == CONSUMER_ERROR){
|
||||
printf("parse pid error\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
printf("miss parameter\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}else if(strcmp(p, "mode") == 0){
|
||||
if(argv[++i]){
|
||||
if(strcmp(argv[i], "file") == 0){
|
||||
g_output_mode = CONSUMER_OUTPUT_MODE_FILE;
|
||||
}else if(strcmp(argv[i], "terminal") == 0){
|
||||
g_output_mode = CONSUMER_OUTPUT_MODE_TERMINAL;
|
||||
}else{
|
||||
printf("invalid option:%s\n",argv[i]);
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}else{
|
||||
printf("miss parameter\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}else if(strcmp(p, "level") == 0){
|
||||
if(argv[++i]){
|
||||
if(parse_log_level(argv[i]) == CONSUMER_ERROR){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}else{
|
||||
printf("miss parameter\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}else if(strcmp(p, "module") == 0){
|
||||
if(argv[++i]){
|
||||
if(strcmp(argv[i], "all") == 0){
|
||||
/*do nothing now*/
|
||||
}else{
|
||||
if(parse_module(argv[i]) == CONSUMER_ERROR){
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
printf("miss parameter\n");
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}else{
|
||||
printf("invalid option:%s\n",p);
|
||||
return CONSUMER_ERROR;
|
||||
}
|
||||
}
|
||||
return CONSUMER_SUCCESS;
|
||||
}
|
||||
void kill_old_process(char *process_name)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char buf[DEFAUT_BUF_SIZE] = {0};
|
||||
char ps_cmd[DEFAULT_COMMAND_LEN] = {0};
|
||||
char kill_cmd[DEFAULT_COMMAND_LEN] = {0};
|
||||
int pid = getpid();
|
||||
int tmp_pid = 0;
|
||||
snprintf(ps_cmd, sizeof(ps_cmd), "ps -ef | grep %s | grep -v grep | awk '{print $2}'", process_name);
|
||||
fp = popen(ps_cmd, "r");
|
||||
if(fp == NULL){
|
||||
return ;
|
||||
}
|
||||
while((fgets(buf, sizeof(buf), fp)) != NULL){
|
||||
tmp_pid = atoi(buf);
|
||||
if(tmp_pid != pid){
|
||||
snprintf(kill_cmd, sizeof(kill_cmd), "kill %d", tmp_pid);
|
||||
system(kill_cmd);
|
||||
}
|
||||
}
|
||||
pclose(fp);
|
||||
return ;
|
||||
}
|
||||
int get_cur_tty_fd()
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char cur_tty_name[DEFAUT_BUF_SIZE] = {0};
|
||||
char *p = NULL;
|
||||
int cur_tty_fd = -1;
|
||||
fp = popen("tty", "r");
|
||||
if(fp == NULL){
|
||||
return -1;
|
||||
}
|
||||
if((fgets(cur_tty_name, sizeof(cur_tty_name), fp)) != NULL){
|
||||
if(strncmp(cur_tty_name, "/dev/pts/", strlen("/dev/pts/")) != 0){
|
||||
goto error;
|
||||
}
|
||||
p = strchr(cur_tty_name, '\n');
|
||||
if(p == NULL){
|
||||
goto error;
|
||||
}
|
||||
*p = '\0';
|
||||
cur_tty_fd = open(cur_tty_name, O_WRONLY);
|
||||
if(cur_tty_fd < 0){
|
||||
goto error;
|
||||
}
|
||||
}else{
|
||||
goto error;
|
||||
}
|
||||
pclose(fp);
|
||||
return cur_tty_fd;
|
||||
|
||||
error:
|
||||
pclose(fp);
|
||||
return -1;
|
||||
}
|
||||
void signal_handler_exit(int signum)
|
||||
{
|
||||
struct shmid_ds buf;
|
||||
if(g_status != NULL){
|
||||
g_status->status = MESA_CONSUMER_NOT_RUNNING;
|
||||
}
|
||||
if(g_shm_overview == NULL || g_shm_overview_id == -1){
|
||||
goto out;
|
||||
}
|
||||
if(shmctl(g_shm_overview_id, IPC_STAT, &buf) == -1){
|
||||
goto out;
|
||||
}
|
||||
if(buf.shm_nattch <= 1){
|
||||
/*
|
||||
This is the last process to use this shared memory,
|
||||
we need to unlink the shared memory before the process exit
|
||||
*/
|
||||
MESA_shm_unlink(g_shm_overview, g_shm_overview_id);
|
||||
}
|
||||
out:
|
||||
exit(0);
|
||||
}
|
||||
/*
|
||||
if we use shmctl function and IPC_RMID parameter,
|
||||
the shared memory will actually be destroyed only after the last process detaches it,
|
||||
but no more attaches for the shared memory identified by the shmid parameter are allowed,
|
||||
producer process and consumer process start at an indeterminate time,
|
||||
so we have to use IPC_RMID parameter to unlink the shared memory when all processes exit
|
||||
*/
|
||||
void register_sginal_handler()
|
||||
{
|
||||
signal(SIGKILL, signal_handler_exit);
|
||||
signal(SIGTRAP, signal_handler_exit);
|
||||
signal(SIGABRT, signal_handler_exit);
|
||||
signal(SIGBUS, signal_handler_exit);
|
||||
signal(SIGFPE, signal_handler_exit);
|
||||
signal(SIGSEGV, signal_handler_exit);
|
||||
signal(SIGTERM, signal_handler_exit);
|
||||
signal(SIGINT, signal_handler_exit);
|
||||
signal(SIGQUIT, signal_handler_exit);
|
||||
return ;
|
||||
}
|
||||
void print_help(char *exe_name)
|
||||
{
|
||||
printf("-pid filter producer process pid, default all\n");
|
||||
printf("-mode set the output to where, default file\n");
|
||||
printf("-level filter log level, default all\n");
|
||||
printf("-module filter module name, support wildcard character '*', default all\n");
|
||||
printf("./%s -pid [ all | '$pid1 $pid2 $pid3...' ] -mode [ file | terminal ] -level [ all | 'debug info fatal...'] -module [ all | 'module module* *module* *module*name*...' ]\n",exe_name);
|
||||
printf("./%s stop exit the consumer process\n", exe_name);
|
||||
return ;
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *exe_name = get_exe_name(argv[0]);
|
||||
if(argc == 2 && argv[1] != NULL && strstr(argv[1], "help") != NULL){
|
||||
print_help(exe_name);
|
||||
return 0;
|
||||
}
|
||||
if(argc == 2 && argv[1] != NULL && (strcmp(argv[1], "stop") == 0)){
|
||||
kill_old_process(exe_name);
|
||||
return 0;
|
||||
}
|
||||
INIT_LIST_HEAD(&g_pid_list.list);
|
||||
INIT_LIST_HEAD(&g_module_list.list);
|
||||
if(get_options(argc, argv) < 0){
|
||||
printf("parse options error\n");
|
||||
return 0;
|
||||
}
|
||||
kill_old_process(exe_name);
|
||||
if(g_output_mode == CONSUMER_OUTPUT_MODE_FILE){
|
||||
consumer_daemo();
|
||||
}else{
|
||||
g_cur_tty_fd = get_cur_tty_fd();
|
||||
}
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
struct MESA_shm_overview *tmp_ovw = NULL;
|
||||
struct MESA_shm_queue_head *ring_queue_head[MESA_SHM_RING_QUEUE_NUM] = {NULL};
|
||||
INIT_LIST_HEAD(&g_log_file_list.list);
|
||||
ret = MESA_shm_alloc_overview(&g_shm_overview, &g_shm_overview_id, &g_status);
|
||||
if(ret < 0){
|
||||
return 0;
|
||||
}
|
||||
g_status->log_level = g_tmp_lv;
|
||||
memset(g_status->pid, 0 ,sizeof(g_status->pid));
|
||||
memset(g_status->module, 0, sizeof(g_status->module));
|
||||
move_pid_list_to_shm(g_status->pid);
|
||||
move_module_list_to_shm(g_status->module);
|
||||
if(g_status->reload_age < ULLONG_MAX){
|
||||
g_status->reload_age ++;
|
||||
}
|
||||
g_status->status = MESA_CONSUMER_RUNNING;
|
||||
register_sginal_handler();
|
||||
init_ring_queue_head_arrray(g_shm_overview, ring_queue_head);
|
||||
while(1){
|
||||
for(i = 0; i< MESA_SHM_RING_QUEUE_NUM; i++){
|
||||
tmp_ovw = g_shm_overview + i;
|
||||
if(tmp_ovw->shmid == -1){
|
||||
break;
|
||||
}
|
||||
if(ring_queue_head[i] == NULL){
|
||||
ring_queue_head[i] = shmat(tmp_ovw->shmid, NULL, 0);
|
||||
if(ring_queue_head[i] == (struct MESA_shm_queue_head *)-1){
|
||||
ring_queue_head[i] = NULL;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
if(!MESA_shm_ring_queue_is_empty(ring_queue_head[i])){
|
||||
if(g_output_mode == CONSUMER_OUTPUT_MODE_FILE){
|
||||
consumer_ring_queue_to_file(ring_queue_head[i]);
|
||||
}else{
|
||||
consumer_ring_queue_to_terminal(ring_queue_head[i], tmp_ovw->producer_pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
usleep(5000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
25
shm_consumer/Makefile
Normal file
25
shm_consumer/Makefile
Normal file
@@ -0,0 +1,25 @@
|
||||
vpath %.h ../inc
|
||||
vpath %.a ../lib
|
||||
|
||||
CC=gcc
|
||||
|
||||
CFLAGS= -g3 -Wall -fPIC -O -Werror
|
||||
CFLAGS+=-I../inc/
|
||||
|
||||
|
||||
LIB=../lib/libMESA_handle_logger.a
|
||||
|
||||
LIB_FILE=$(wildcard ../lib/*.a)
|
||||
|
||||
SRC=MESA_shm_consumer.c
|
||||
TARGET=MESA_shm_consumer
|
||||
|
||||
all:$(TARGET)
|
||||
|
||||
$(TARGET):$(SRC) $(LIB_FILE)
|
||||
$(CC) $(CFLAGS) $(INC) $(LIBPATH) $< $(LIB) -o $@
|
||||
|
||||
clean :
|
||||
rm -f $(TARGET)
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
#include "MESA_handle_logger.h"
|
||||
#include "MESA_shm_ring_queue.h"
|
||||
#include "snprintf.h"
|
||||
#include <pthread.h>
|
||||
#include "zlog.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define MAX_HANDLE_LOG_PATH 4096
|
||||
static int g_zlog_inited = 0;
|
||||
@@ -14,12 +20,37 @@ static int g_zlog_conf_fp = -1;
|
||||
static char global_conf_filepath[MAX_HANDLE_LOG_PATH] = "";
|
||||
static char tmp_conf_filepath[MAX_HANDLE_LOG_PATH] = "";
|
||||
|
||||
#define MESA_FORMAT_RULE_MAX 16
|
||||
#define MESA_PTHREAD_FMT_BUF_DEFAULT_LEN 256
|
||||
|
||||
pthread_key_t MESA_pthread_key;
|
||||
pthread_once_t MESA_pthread_key_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
|
||||
struct MESA_pthread_private{
|
||||
char *fmt_buf;
|
||||
char *cache_buf;
|
||||
int fmt_buf_len;
|
||||
int cache_buf_len;
|
||||
struct MESA_shm_queue_head *ring_queue_head;
|
||||
};
|
||||
struct MESA_fmt_obj{
|
||||
char sign;
|
||||
MESA_fmt_handler handler;
|
||||
};
|
||||
|
||||
typedef struct log_handle_t
|
||||
{
|
||||
int runtime_log_level;
|
||||
int fmt_rule_num;
|
||||
zlog_category_t *zc;
|
||||
const char *global_conf_path;
|
||||
char runtime_log_file[MAX_HANDLE_LOG_PATH];
|
||||
int fmt_buf_len;
|
||||
struct MESA_fmt_obj fmt_rule[MESA_FORMAT_RULE_MAX];
|
||||
char real_log_file[MESA_SHM_LOG_PATH_LEN];
|
||||
int real_log_file_len;
|
||||
int pid;
|
||||
} log_handle_t;
|
||||
|
||||
|
||||
@@ -116,7 +147,7 @@ static void escape_for_zlog(char *in_buf, int buflen)
|
||||
return;
|
||||
}
|
||||
|
||||
static const char *loglevel_to_name(int level)
|
||||
const char *loglevel_to_name(int level)
|
||||
{
|
||||
if (level <= RLOG_LV_DEBUG)
|
||||
return "DEBUG";
|
||||
@@ -127,6 +158,7 @@ static const char *loglevel_to_name(int level)
|
||||
|
||||
static void snapshot_handle_info(const char *handle_name, const char *log_path, int level)
|
||||
{
|
||||
int n = 0;
|
||||
char zlog_rule_conf_content[MAX_HANDLE_LOG_PATH + 1] = "";
|
||||
static char *tmp_prefix = "/tmp/MESA_handle_logger/";
|
||||
static char *zlog_conf_init_buff = "[global]\ndefault format = \"%d(%c), %V, %F, %U, %m%n\" \n[levels]\nDEBUG=10\nINFO=20\nFATAL=30\n[rules]";
|
||||
@@ -145,13 +177,22 @@ static void snapshot_handle_info(const char *handle_name, const char *log_path,
|
||||
{
|
||||
return;
|
||||
}
|
||||
write(g_zlog_conf_fp, zlog_conf_init_buff, strlen(zlog_conf_init_buff));
|
||||
n = write(g_zlog_conf_fp, zlog_conf_init_buff, strlen(zlog_conf_init_buff));
|
||||
if(n < 0){
|
||||
fprintf(stderr,"write g_zlog_conf_fp error, func=%s, line=%d\n",__FUNCTION__, __LINE__);
|
||||
}
|
||||
fsync(g_zlog_conf_fp);
|
||||
}
|
||||
snprintf(zlog_rule_conf_content, sizeof(zlog_rule_conf_content),
|
||||
n = snprintf(zlog_rule_conf_content, sizeof(zlog_rule_conf_content),
|
||||
"\n%s.%s \"%s.%%d(%%F)\"",
|
||||
handle_name, loglevel_to_name(level), log_path);
|
||||
write(g_zlog_conf_fp, zlog_rule_conf_content, strlen(zlog_rule_conf_content));
|
||||
if(n > 0){
|
||||
n = write(g_zlog_conf_fp, zlog_rule_conf_content, strlen(zlog_rule_conf_content));
|
||||
if(n < 0){
|
||||
fprintf(stderr,"write g_zlog_conf_fp error, func=%s, line=%d\n",__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
fsync(g_zlog_conf_fp);
|
||||
|
||||
if(g_zlog_inited == 0)
|
||||
@@ -162,15 +203,78 @@ static void snapshot_handle_info(const char *handle_name, const char *log_path,
|
||||
}
|
||||
return;
|
||||
}
|
||||
struct MESA_pthread_private *MESA_create_pthread_private(void *handle)
|
||||
{
|
||||
|
||||
log_handle_t *p_handle = (log_handle_t *)handle;
|
||||
struct MESA_pthread_private *pri = malloc(sizeof(struct MESA_pthread_private));
|
||||
if(pri == NULL){
|
||||
return NULL;
|
||||
}
|
||||
memset(pri, 0 ,sizeof(struct MESA_pthread_private));
|
||||
pri->fmt_buf = malloc(p_handle->fmt_buf_len);
|
||||
if(pri->fmt_buf == NULL){
|
||||
goto error;
|
||||
}
|
||||
pri->fmt_buf_len = p_handle->fmt_buf_len;
|
||||
pri->cache_buf_len = MESA_SHM_RING_QUEUE_BLOCK_SIZE;
|
||||
pri->cache_buf = malloc(pri->cache_buf_len);
|
||||
if(pri->cache_buf == NULL){
|
||||
goto error;
|
||||
}
|
||||
pri->ring_queue_head = MESA_shm_get_ring_queue(p_handle->pid);
|
||||
if(pri->ring_queue_head == NULL){
|
||||
goto error;
|
||||
}
|
||||
return pri;
|
||||
|
||||
error:
|
||||
if(pri->fmt_buf){
|
||||
free(pri->fmt_buf);
|
||||
}
|
||||
if(pri->cache_buf){
|
||||
free(pri->cache_buf);
|
||||
}
|
||||
if(pri){
|
||||
free(pri);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
void MESA_free_pthread_private(void *arg)
|
||||
{
|
||||
struct MESA_pthread_private *pri = (struct MESA_pthread_private *)arg;
|
||||
if(pri == NULL){
|
||||
return ;
|
||||
}
|
||||
if(pri->ring_queue_head){
|
||||
MESA_shm_recycle_ring_queue(pri->ring_queue_head);
|
||||
}
|
||||
if(pri->fmt_buf){
|
||||
free(pri->fmt_buf);
|
||||
}
|
||||
if(pri->cache_buf){
|
||||
free(pri->cache_buf);
|
||||
}
|
||||
if(pri){
|
||||
free(pri);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
void MESA_alloc_pthread_key()
|
||||
{
|
||||
pthread_key_create(&MESA_pthread_key, MESA_free_pthread_private);
|
||||
MESA_shm_init();
|
||||
return ;
|
||||
}
|
||||
|
||||
void *MESA_create_runtime_log_handle(const char *file_path, int level)
|
||||
{
|
||||
if(file_path == NULL)
|
||||
return NULL;
|
||||
|
||||
int rc = -1;
|
||||
char rpath[PATH_MAX] = {0};
|
||||
int rpath_len = 0;
|
||||
zlog_category_t *zc = NULL;
|
||||
FILE *fp = NULL;
|
||||
log_handle_t *p_handle = NULL;
|
||||
|
||||
char handle_name[MAX_HANDLE_LOG_PATH];
|
||||
@@ -199,6 +303,15 @@ void *MESA_create_runtime_log_handle(const char *file_path, int level)
|
||||
strncpy(p_handle->runtime_log_file, file_path, sizeof(p_handle->runtime_log_file) - 1);
|
||||
p_handle->runtime_log_level = level;
|
||||
p_handle->zc = zc;
|
||||
p_handle->fmt_buf_len = MESA_PTHREAD_FMT_BUF_DEFAULT_LEN;
|
||||
realpath(file_path, rpath);
|
||||
rpath_len = strlen(rpath);
|
||||
if(rpath_len < MESA_SHM_LOG_PATH_LEN){
|
||||
memcpy(p_handle->real_log_file, rpath, rpath_len);
|
||||
p_handle->real_log_file_len = rpath_len;
|
||||
}
|
||||
p_handle->pid = getpid();
|
||||
pthread_once(&MESA_pthread_key_once, MESA_alloc_pthread_key);
|
||||
return (void *)p_handle;
|
||||
}
|
||||
|
||||
@@ -216,18 +329,51 @@ void MESA_destroy_runtime_log_handle(void *handle)
|
||||
|
||||
void MESA_handle_runtime_log(void *handle, int level, const char *module, const char *fmt, ...)
|
||||
{
|
||||
|
||||
log_handle_t *p_handle = (log_handle_t *)handle;
|
||||
|
||||
if(p_handle == NULL || p_handle->runtime_log_file == NULL)return;
|
||||
|
||||
if(p_handle->zc == NULL)return;
|
||||
|
||||
int n = 0;
|
||||
int payload_len = 0;
|
||||
struct MESA_shm_log_buf lbuf;
|
||||
if(p_handle == NULL || p_handle->runtime_log_file == NULL || module == NULL){
|
||||
return;
|
||||
}
|
||||
if(p_handle->zc == NULL){
|
||||
return;
|
||||
}
|
||||
struct MESA_pthread_private *pri = (struct MESA_pthread_private *)pthread_getspecific(MESA_pthread_key);
|
||||
int match_result = MESA_shm_match_consumer_rule(level, p_handle->pid, module);
|
||||
if(match_result != MESA_MATCH_CONSUMER_RULE_SUCCESS){
|
||||
if(pri != NULL && match_result == MESA_MATCH_CONSUMER_RULE_FAIL_PID){
|
||||
/*the process pid will not be consumed, we need to free the memory and recycle the ring queue*/
|
||||
MESA_free_pthread_private(pri);
|
||||
pthread_setspecific(MESA_pthread_key,(void*)NULL);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vzlog(p_handle->zc, p_handle->runtime_log_file, strlen(p_handle->runtime_log_file), module, strlen(module), __LINE__, level, fmt, ap);
|
||||
va_end(ap);
|
||||
return ;
|
||||
va_start(ap, fmt);
|
||||
if(pri == NULL){
|
||||
pri = MESA_create_pthread_private(handle);
|
||||
if(pri == NULL){
|
||||
return ;
|
||||
}
|
||||
pthread_setspecific(MESA_pthread_key,(void*)pri);
|
||||
}
|
||||
if(MESA_shm_ring_queue_is_full(pri->ring_queue_head)){
|
||||
return ;
|
||||
}
|
||||
n = portable_vsnprintf(handle, pri->cache_buf, pri->cache_buf_len, fmt, ap);
|
||||
payload_len = n < (pri->cache_buf_len - 1) ? n : (pri->cache_buf_len - 1);
|
||||
lbuf.buf = pri->cache_buf;
|
||||
lbuf.buflen = payload_len;
|
||||
lbuf.level = level;
|
||||
lbuf.log_file = p_handle->real_log_file;
|
||||
lbuf.log_file_len = p_handle->real_log_file_len;
|
||||
lbuf.module = (char *)module;
|
||||
lbuf.module_len = strlen(module);
|
||||
MESA_shm_copy_buf_to_ring_queue(pri->ring_queue_head, &lbuf);
|
||||
//zlog(p_handle->zc, p_handle->runtime_log_file, strlen(p_handle->runtime_log_file), module, strlen(module), __LINE__, level, "%s", pri->cache_buf);
|
||||
va_end(ap);
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
@@ -328,3 +474,51 @@ void MESA_handle_runtime_log_destruction()
|
||||
close(g_zlog_conf_fp);
|
||||
}
|
||||
}
|
||||
|
||||
int MESA_handle_fmt_rule_register(void *handle, char sign, MESA_fmt_handler handler, int buflen)
|
||||
{
|
||||
log_handle_t *p_handle = (log_handle_t *)handle;
|
||||
int fmt_rule_num = p_handle->fmt_rule_num;
|
||||
if(fmt_rule_num >= MESA_FORMAT_RULE_MAX){
|
||||
return -1;
|
||||
}
|
||||
p_handle->fmt_rule[fmt_rule_num].sign = sign;
|
||||
p_handle->fmt_rule[fmt_rule_num].handler = handler;
|
||||
p_handle->fmt_rule_num++;
|
||||
if(buflen > 0 && buflen > p_handle->fmt_buf_len){
|
||||
p_handle->fmt_buf_len = buflen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int MESA_handle_check_fmt_sign(void *handle, char sign, MESA_fmt_handler *handler, char**buf, int *maxlen)
|
||||
{
|
||||
if(!handle || !handler){
|
||||
return 0;
|
||||
}
|
||||
struct MESA_pthread_private *pthread_pri = NULL;
|
||||
log_handle_t *p_handle = (log_handle_t *)handle;
|
||||
int fmt_rule_num = p_handle->fmt_rule_num;
|
||||
int i = 0;
|
||||
for(i = 0; i < fmt_rule_num; i++){
|
||||
if(p_handle->fmt_rule[i].sign == sign){
|
||||
pthread_pri = (struct MESA_pthread_private *)pthread_getspecific(MESA_pthread_key);
|
||||
if(pthread_pri == NULL){
|
||||
return 0;
|
||||
}
|
||||
*handler = p_handle->fmt_rule[i].handler;
|
||||
if(p_handle->fmt_buf_len > pthread_pri->fmt_buf_len){
|
||||
char *p = realloc(pthread_pri->fmt_buf, p_handle->fmt_buf_len);
|
||||
if(p != NULL){
|
||||
pthread_pri->fmt_buf = p;
|
||||
pthread_pri->fmt_buf_len = p_handle->fmt_buf_len;
|
||||
}
|
||||
}
|
||||
*buf = pthread_pri->fmt_buf;
|
||||
*maxlen = pthread_pri->fmt_buf_len;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
475
src/MESA_shm_ring_queue.c
Normal file
475
src/MESA_shm_ring_queue.c
Normal file
@@ -0,0 +1,475 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include "MESA_handle_logger.h"
|
||||
#include "MESA_shm_ring_queue.h"
|
||||
#include "list.h"
|
||||
|
||||
struct MESA_shm_overview *MESA_shm_ovw = NULL;
|
||||
struct MESA_shm_queue_head *MESA_ring_queue_head[MESA_SHM_RING_QUEUE_NUM] = {NULL};
|
||||
int MESA_shm_ovw_id = -1;
|
||||
struct MESA_shm_consumer *MESA_consumer_status = NULL;
|
||||
|
||||
|
||||
void MESA_shm_init_ring_queue_mutex(struct MESA_shm_overview *ovw)
|
||||
{
|
||||
pthread_mutexattr_t mutexattr;
|
||||
pthread_mutexattr_init(&mutexattr);
|
||||
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
|
||||
pthread_mutexattr_setrobust(&mutexattr, PTHREAD_MUTEX_ROBUST);
|
||||
pthread_mutex_init(&ovw->mutex, &mutexattr);
|
||||
return ;
|
||||
}
|
||||
void MESA_shm_unlink(struct MESA_shm_overview *ovw, int ovw_shmid)
|
||||
{
|
||||
int i;
|
||||
struct MESA_shm_overview *tmp_ovw;
|
||||
if(ovw == NULL || ovw_shmid == -1){
|
||||
return ;
|
||||
}
|
||||
for(i = 0; i < MESA_SHM_RING_QUEUE_NUM; i++){
|
||||
tmp_ovw = ovw + i;
|
||||
if(tmp_ovw->shmid != -1){
|
||||
shmctl(tmp_ovw->shmid, IPC_RMID, NULL);
|
||||
}
|
||||
}
|
||||
shmctl(ovw_shmid, IPC_RMID, NULL);
|
||||
return ;
|
||||
}
|
||||
void MESA_shm_check_unlink(int signum)
|
||||
{
|
||||
struct shmid_ds buf;
|
||||
if(MESA_shm_ovw == NULL || MESA_shm_ovw_id == -1){
|
||||
goto out;
|
||||
}
|
||||
if(shmctl(MESA_shm_ovw_id, IPC_STAT, &buf) == -1){
|
||||
goto out;
|
||||
}
|
||||
if(buf.shm_nattch <= 1){
|
||||
/*
|
||||
This is the last process to use this shared memory,
|
||||
we need to unlink the shared memory before the process exit
|
||||
*/
|
||||
MESA_shm_unlink(MESA_shm_ovw, MESA_shm_ovw_id);
|
||||
}
|
||||
out:
|
||||
exit(0);
|
||||
}
|
||||
/*
|
||||
if we use shmctl function and IPC_RMID parameter,
|
||||
the shared memory will actually be destroyed only after the last process detaches it,
|
||||
but no more attaches for the shared memory identified by the shmid parameter are allowed,
|
||||
producer process and consumer process start at an indeterminate time,
|
||||
so we have to use IPC_RMID parameter to unlink the shared memory when all processes exit
|
||||
*/
|
||||
void MESA_shm_register_sginal_handler()
|
||||
{
|
||||
signal(SIGKILL, MESA_shm_check_unlink);
|
||||
signal(SIGTRAP, MESA_shm_check_unlink);
|
||||
signal(SIGABRT, MESA_shm_check_unlink);
|
||||
signal(SIGBUS, MESA_shm_check_unlink);
|
||||
signal(SIGFPE, MESA_shm_check_unlink);
|
||||
signal(SIGSEGV, MESA_shm_check_unlink);
|
||||
signal(SIGTERM, MESA_shm_check_unlink);
|
||||
signal(SIGINT, MESA_shm_check_unlink);
|
||||
signal(SIGQUIT, MESA_shm_check_unlink);
|
||||
return ;
|
||||
}
|
||||
int MESA_shm_alloc_overview(struct MESA_shm_overview **ovw, int *ovw_id, struct MESA_shm_consumer **consumer_status)
|
||||
{
|
||||
int shmsize = (sizeof(struct MESA_shm_overview) * MESA_SHM_RING_QUEUE_NUM) + sizeof(struct MESA_shm_consumer);
|
||||
struct MESA_shm_overview *shm_overview = NULL;
|
||||
struct MESA_shm_overview *tmp_ovw = NULL;
|
||||
int i = 0;
|
||||
int shmid = shmget(MESA_SHM_KEY_OVERVIEW, 0, 0);
|
||||
if(shmid == -1){
|
||||
shmid = shmget(MESA_SHM_KEY_OVERVIEW, shmsize, (SHM_R|SHM_W|IPC_CREAT));
|
||||
if(shmid == -1){
|
||||
return -1;
|
||||
}else{
|
||||
shm_overview = (struct MESA_shm_overview *)shmat(shmid, NULL, 0);
|
||||
if(shm_overview == (struct MESA_shm_overview *)-1){
|
||||
return -1;
|
||||
}
|
||||
memset((void *)shm_overview, 0, shmsize);
|
||||
for(i = 0; i < MESA_SHM_RING_QUEUE_NUM; i++){
|
||||
tmp_ovw = shm_overview + i;
|
||||
tmp_ovw->shmkey = i + MESA_SHM_KEY_MIN;
|
||||
tmp_ovw->shmid = -1;
|
||||
tmp_ovw->idx = i;
|
||||
MESA_shm_init_ring_queue_mutex(tmp_ovw);
|
||||
}
|
||||
*ovw = shm_overview;
|
||||
*ovw_id = shmid;
|
||||
*consumer_status = (struct MESA_shm_consumer *)(shm_overview + MESA_SHM_RING_QUEUE_NUM);
|
||||
|
||||
}
|
||||
}else{
|
||||
shm_overview = (struct MESA_shm_overview *)shmat(shmid, NULL, 0);
|
||||
if(shm_overview == (struct MESA_shm_overview *)-1){
|
||||
return -1;
|
||||
}
|
||||
*ovw = shm_overview;
|
||||
*ovw_id = shmid;
|
||||
*consumer_status = (struct MESA_shm_consumer *)(shm_overview + MESA_SHM_RING_QUEUE_NUM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void MESA_shm_init()
|
||||
{
|
||||
if(MESA_shm_ovw == NULL || MESA_consumer_status == NULL || MESA_shm_ovw_id == -1){
|
||||
MESA_shm_alloc_overview(&MESA_shm_ovw, &MESA_shm_ovw_id, &MESA_consumer_status);
|
||||
}
|
||||
MESA_shm_register_sginal_handler();
|
||||
return ;
|
||||
|
||||
}
|
||||
void MESA_shm_init_new_ring_queue(struct MESA_shm_queue_head *head, struct MESA_shm_overview *ovw)
|
||||
{
|
||||
head->blksize = MESA_SHM_RING_QUEUE_BLOCK_SIZE;
|
||||
head->blknum = MESA_SHM_RING_QUEUE_BLOCK_NUM;
|
||||
head->rd_idx = 0;
|
||||
head->wr_idx = 0;
|
||||
head->ovw_idx = ovw->idx;
|
||||
return ;
|
||||
}
|
||||
void MESA_shm_recycle_ring_queue(struct MESA_shm_queue_head *ring_queue_head)
|
||||
{
|
||||
if(MESA_shm_ovw == NULL){
|
||||
return ;
|
||||
}
|
||||
struct MESA_shm_overview *ovw = MESA_shm_ovw + ring_queue_head->ovw_idx;
|
||||
pthread_mutex_unlock(&ovw->mutex);
|
||||
return ;
|
||||
}
|
||||
int MESA_shm_mutex_trylock_success(struct MESA_shm_overview *ovw)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = pthread_mutex_trylock(&ovw->mutex);
|
||||
if(ret == 0){
|
||||
return 1;
|
||||
}else if(ret == EOWNERDEAD){
|
||||
ret = pthread_mutex_consistent(&ovw->mutex);
|
||||
if(ret == 0){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
struct MESA_shm_queue_head *MESA_shm_alloc_new_ring_queue(struct MESA_shm_overview *ovw)
|
||||
{
|
||||
struct MESA_shm_queue_head *head = NULL;
|
||||
int shmid = -1;
|
||||
int shmsize = sizeof(struct MESA_shm_queue_head) + (MESA_SHM_RING_QUEUE_BLOCK_NUM * MESA_SHM_RING_QUEUE_BLOCK_SIZE);
|
||||
shmid = shmget(ovw->shmkey, 0, 0);
|
||||
if(shmid == -1){
|
||||
shmid = shmget(ovw->shmkey, shmsize, (SHM_R|SHM_W|IPC_CREAT));
|
||||
if(shmid == -1){
|
||||
return NULL;
|
||||
}
|
||||
head = (struct MESA_shm_queue_head *)shmat(shmid, NULL, 0);
|
||||
}else{
|
||||
head = (struct MESA_shm_queue_head *)shmat(shmid, NULL, 0);
|
||||
}
|
||||
if(head == (struct MESA_shm_queue_head *)-1){
|
||||
return NULL;
|
||||
}
|
||||
MESA_shm_init_new_ring_queue(head, ovw);
|
||||
ovw->shmid = shmid;
|
||||
MESA_ring_queue_head[ovw->idx] = head;
|
||||
return head;
|
||||
}
|
||||
|
||||
struct MESA_shm_queue_head *MESA_shm_try_reuse_ring_queue(struct MESA_shm_overview *ovw)
|
||||
{
|
||||
struct MESA_shm_queue_head *head = NULL;
|
||||
if(MESA_ring_queue_head[ovw->idx] != NULL){
|
||||
head = MESA_ring_queue_head[ovw->idx];
|
||||
}else{
|
||||
head = (struct MESA_shm_queue_head *)shmat(ovw->shmid, NULL, 0);
|
||||
if(head == (struct MESA_shm_queue_head *)-1){
|
||||
return NULL;
|
||||
}
|
||||
MESA_ring_queue_head[ovw->idx] = head;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
struct MESA_shm_queue_head *MESA_shm_get_ring_queue(int pid)
|
||||
{
|
||||
int i = 0;
|
||||
struct MESA_shm_overview *tmp_ovw = NULL;
|
||||
struct MESA_shm_queue_head *head = NULL;
|
||||
if(MESA_shm_ovw == NULL){
|
||||
return NULL;
|
||||
}
|
||||
for(i = 0; i < MESA_SHM_RING_QUEUE_NUM; i++){
|
||||
tmp_ovw = MESA_shm_ovw + i;
|
||||
if(!MESA_shm_mutex_trylock_success(tmp_ovw)){
|
||||
continue ;
|
||||
}
|
||||
if(tmp_ovw->shmid == -1){
|
||||
head = MESA_shm_alloc_new_ring_queue(tmp_ovw);
|
||||
if(head == NULL){
|
||||
pthread_mutex_unlock(&tmp_ovw->mutex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
head = MESA_shm_try_reuse_ring_queue(tmp_ovw);
|
||||
if(head != NULL){
|
||||
break ;
|
||||
}else{
|
||||
pthread_mutex_unlock(&tmp_ovw->mutex);
|
||||
continue ;
|
||||
}
|
||||
}
|
||||
if(head != NULL){
|
||||
tmp_ovw->producer_pid = pid;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
int MESA_shm_ring_queue_is_empty(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
if(head->rd_idx == head->wr_idx){
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int MESA_shm_ring_queue_is_full(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
if(((head->wr_idx + 1) % head->blknum) == head->rd_idx){
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void *MESA_shm_ring_queue_get_read_pos(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
return (void *)((char *)(head + 1) + (head->blksize * head->rd_idx));
|
||||
}
|
||||
|
||||
void *MESA_shm_ring_queue_get_write_pos(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
return (void *)((char *)(head + 1) + (head->blksize * head->wr_idx));
|
||||
}
|
||||
|
||||
void MESA_shm_ring_queue_push_read_pos(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
head->rd_idx = (head->rd_idx + 1) % head->blknum;
|
||||
}
|
||||
|
||||
void MESA_shm_ring_queue_push_write_pos(struct MESA_shm_queue_head *head)
|
||||
{
|
||||
head->wr_idx = (head->wr_idx + 1) % head->blknum;
|
||||
}
|
||||
|
||||
/*
|
||||
(int)log_file_len + (str)log_file + '\0' + (int)level + (int)module_len + (str)module + '\0' + (int)payload_len + (str)payload + '\0'
|
||||
*/
|
||||
int MESA_shm_copy_buf_to_ring_queue(struct MESA_shm_queue_head *head, struct MESA_shm_log_buf *lbuf)
|
||||
{
|
||||
int available_len = 0, payload_len = 0;
|
||||
int *pi = NULL;;
|
||||
char *pc = NULL;
|
||||
if(head == NULL || lbuf == NULL){
|
||||
return 0;
|
||||
}
|
||||
if(MESA_shm_ring_queue_is_full(head)){
|
||||
return 0;
|
||||
}
|
||||
if(lbuf->log_file_len <= 0 || lbuf->log_file_len >= MESA_SHM_LOG_PATH_LEN){
|
||||
return 0;
|
||||
}
|
||||
available_len = MESA_SHM_RING_QUEUE_BLOCK_SIZE - lbuf->module_len - lbuf->log_file_len -(4 * sizeof(int)) - 3;
|
||||
if(available_len <= 0){
|
||||
return 0;
|
||||
}
|
||||
payload_len = lbuf->buflen < available_len ? lbuf->buflen : available_len;
|
||||
/*(int)log_file_len*/
|
||||
pi = (int *)MESA_shm_ring_queue_get_write_pos(head);
|
||||
*pi = lbuf->log_file_len;
|
||||
|
||||
/*(str)log_file*/
|
||||
pc = (char *)(pi + 1);
|
||||
memcpy(pc, lbuf->log_file, lbuf->log_file_len);
|
||||
|
||||
/*'\0'*/
|
||||
pc = pc + lbuf->log_file_len;
|
||||
*pc = '\0';
|
||||
|
||||
/*(int)level*/
|
||||
pi = (int *)(pc + 1);
|
||||
*pi = lbuf->level;
|
||||
|
||||
/*(int)module_len*/
|
||||
pi = pi + 1;
|
||||
*pi = lbuf->module_len;
|
||||
|
||||
/*(str)module*/
|
||||
pc = (char *)(pi + 1);
|
||||
memcpy(pc, lbuf->module, lbuf->module_len);
|
||||
|
||||
/*'\0'*/
|
||||
pc = pc + lbuf->module_len;
|
||||
*pc = '\0';
|
||||
|
||||
/*(int)payload_len*/
|
||||
pi = (int *)(pc + 1);
|
||||
*pi = payload_len;
|
||||
|
||||
/*(str)payload*/
|
||||
pc = (char *)(pi + 1);
|
||||
memcpy(pc, lbuf->buf, payload_len);
|
||||
|
||||
/*'\0'*/
|
||||
pc = pc + payload_len;
|
||||
*pc = '\0';
|
||||
|
||||
MESA_shm_ring_queue_push_write_pos(head);
|
||||
return payload_len;
|
||||
}
|
||||
|
||||
int MESA_shm_match_level(int level)
|
||||
{
|
||||
if(MESA_consumer_status->log_level == MESA_CONSUMER_LEVEL_ALL){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}
|
||||
switch (level) {
|
||||
case RLOG_LV_DEBUG:
|
||||
if(MESA_consumer_status->log_level & MESA_CONSUMER_LEVEL_DEBUG){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}else{
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
break;
|
||||
case RLOG_LV_INFO:
|
||||
if(MESA_consumer_status->log_level & MESA_CONSUMER_LEVEL_INFO){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}else{
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
break;
|
||||
case RLOG_LV_FATAL:
|
||||
if(MESA_consumer_status->log_level & MESA_CONSUMER_LEVEL_FATAL){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}else{
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
int MESA_shm_match_pid(int *p_pid, int cur_pid)
|
||||
{
|
||||
int i = 0;
|
||||
if(p_pid[0] == 0){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}
|
||||
for(i = 0; i < MESA_SHM_PID_NUM; i++){
|
||||
if(p_pid[i] == 0){
|
||||
break ;
|
||||
}
|
||||
if(p_pid[i] == cur_pid){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}
|
||||
}
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
int MESA_shm_string_match_success(char *src, int src_len, char *pattern, int pattern_len) {
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int start_pos = -1;
|
||||
int match = -1;
|
||||
while(i < src_len){
|
||||
if(j < pattern_len && src[i] == pattern[j]){
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
else if(j < pattern_len && pattern[j] == '*'){
|
||||
start_pos = j;
|
||||
match = i;
|
||||
j = start_pos + 1;
|
||||
}
|
||||
else if(start_pos != -1){
|
||||
match++;
|
||||
i = match;
|
||||
j = start_pos + 1;
|
||||
}
|
||||
else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
while(j < pattern_len && pattern[j] == '*'){
|
||||
j++ ;
|
||||
};
|
||||
return (j==pattern_len);
|
||||
}
|
||||
|
||||
int MESA_shm_match_module(const char *module)
|
||||
{
|
||||
int i;
|
||||
int shn_module_len;
|
||||
char *shm_module;
|
||||
int enable = *((int *)MESA_consumer_status->module[0]);
|
||||
if(enable == 0){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}
|
||||
for(i = 0; i < MESA_SHM_MODULE_NUM; i++){
|
||||
shn_module_len = *((int *)MESA_consumer_status->module[i]);
|
||||
if(shn_module_len == 0){
|
||||
break ;
|
||||
}
|
||||
shm_module = MESA_consumer_status->module[i] + sizeof(int);
|
||||
if(MESA_shm_string_match_success((char *)module, (int)strlen(module), shm_module, shn_module_len)){
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}
|
||||
}
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
int MESA_shm_match_consumer_rule(int level, int pid, const char *module)
|
||||
{
|
||||
static __thread unsigned long long reload_age = 0;
|
||||
static __thread int last_pid_match_result = MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
if(MESA_consumer_status == NULL){
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
}
|
||||
if(MESA_consumer_status->status == MESA_CONSUMER_NOT_RUNNING){
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL_STATUS;
|
||||
}
|
||||
if(MESA_shm_match_level(level) == MESA_MATCH_CONSUMER_RULE_FAIL){
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL_LEVEL;
|
||||
}
|
||||
/*
|
||||
only need to match pid once after consumer or producer restart,
|
||||
in order to improve matching speed for producer process
|
||||
*/
|
||||
if(MESA_consumer_status->reload_age != reload_age){
|
||||
reload_age = MESA_consumer_status->reload_age;
|
||||
if(MESA_shm_match_pid(MESA_consumer_status->pid, pid) == MESA_MATCH_CONSUMER_RULE_FAIL){
|
||||
last_pid_match_result = MESA_MATCH_CONSUMER_RULE_FAIL;
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL_PID;
|
||||
}
|
||||
last_pid_match_result = MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}else{
|
||||
if(last_pid_match_result == MESA_MATCH_CONSUMER_RULE_FAIL){
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL_PID;
|
||||
}
|
||||
}
|
||||
if(MESA_shm_match_module(module) == MESA_MATCH_CONSUMER_RULE_FAIL){
|
||||
return MESA_MATCH_CONSUMER_RULE_FAIL_MODULE;
|
||||
}
|
||||
|
||||
return MESA_MATCH_CONSUMER_RULE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ MAKE_TIME=$(shell date "+%Y_%m_%d_%H")
|
||||
VERSION_FLAGS += -DGIT_VERSION=1_0_0_$(GIT_BRANCH)_$(GIT_SHA1)_$(MAKE_TIME)
|
||||
|
||||
CFLAGS += ${VERSION_FLAGS}
|
||||
CFLAGS += -DHAVE_SNPRINTF -DPREFER_PORTABLE_SNPRINTF -DSNPRINTF_LONGLONG_SUPPORT
|
||||
CXXFLAGS += ${VERSION_FLAGS}
|
||||
|
||||
ifdef ASAN
|
||||
@@ -16,7 +17,7 @@ CFLAGS_+= -fsanitize=address -fno-omit-frame-pointer
|
||||
LIB+=-lasan
|
||||
endif
|
||||
|
||||
SRC=MESA_handle_logger.c
|
||||
SRC=MESA_handle_logger.c MESA_shm_ring_queue.c portable_snprintf/snprintf.c
|
||||
OBJS=$(SRC:.c=.o)
|
||||
|
||||
TARGET=libMESA_handle_logger.a libMESA_handle_logger.so
|
||||
@@ -25,10 +26,10 @@ all:$(TARGET)
|
||||
cp -f $(TARGET) ../lib
|
||||
|
||||
libMESA_handle_logger.a:$(OBJS)
|
||||
ar cqs $@ $<
|
||||
ar cqs $@ $^
|
||||
|
||||
libMESA_handle_logger.so:$(OBJS)
|
||||
$(CC) $(CFLAGS) $(LIB) -shared $< -o $@
|
||||
$(CC) $(CFLAGS) $(LIB) -shared $^ -o $@
|
||||
.c.o:
|
||||
|
||||
#$(OBJS):$(SRC)
|
||||
|
||||
121
src/portable_snprintf/LICENSE.txt
Normal file
121
src/portable_snprintf/LICENSE.txt
Normal file
@@ -0,0 +1,121 @@
|
||||
The Frontier Artistic License Version 1.0
|
||||
Derived from the Artistic License at OpenSource.org.
|
||||
Submitted to OpenSource.org for Open Source Initiative certification.
|
||||
|
||||
Preamble
|
||||
|
||||
The intent of this document is to state the conditions under which a
|
||||
Package may be copied, such that the Copyright Holder maintains some
|
||||
semblance of artistic control over the development of the package,
|
||||
while giving the users of the package the right to use and distribute
|
||||
the Package in a more-or-less customary fashion, plus the right to
|
||||
make reasonable modifications.
|
||||
|
||||
Definitions
|
||||
|
||||
"Package" refers to the script, suite, file, or collection of
|
||||
scripts, suites, and/or files distributed by the Copyright Holder,
|
||||
and to derivatives of that Package created through textual modification.
|
||||
|
||||
"Standard Version" refers to such a Package if it has not been
|
||||
modified, or has been modified in accordance with the wishes of
|
||||
the Copyright Holder.
|
||||
|
||||
"Copyright Holder" is whoever is named in the copyright statement
|
||||
or statements for the package.
|
||||
|
||||
"You" is you, if you're thinking about copying or distributing
|
||||
this Package.
|
||||
|
||||
"Reasonable copying fee" is whatever you can justify on the basis
|
||||
of media cost, duplication charges, time of people involved, and
|
||||
so on. (You will not be required to justify it to the Copyright
|
||||
Holder, but only to the computing community at large as a market
|
||||
that must bear the fee.)
|
||||
|
||||
"Freely Available" means that no fee is charged for the item
|
||||
itself, though there may be fees involved in handling the item.
|
||||
It also means that recipients of the item may redistribute it under
|
||||
the same conditions they received it.
|
||||
|
||||
|
||||
Terms
|
||||
|
||||
1. You may make and give away verbatim copies of the source form of
|
||||
the Standard Version of this Package without restriction, provided
|
||||
that you duplicate all of the original copyright notices and
|
||||
associated disclaimers.
|
||||
|
||||
2. You may apply bug fixes, portability fixes, and other modifications
|
||||
derived from the Public Domain or from the Copyright Holder. A Package
|
||||
modified in such a way shall still be considered the Standard Version.
|
||||
|
||||
3. You may otherwise modify your copy of this Package in any way,
|
||||
provided that you insert a prominent notice in each changed script,
|
||||
suite, or file stating how and when you changed that script, suite,
|
||||
or file, and provided that you do at least ONE of the following:
|
||||
|
||||
a) Use the modified Package only within your corporation or
|
||||
organization, or retain the modified Package solely for personal use.
|
||||
|
||||
b) Place your modifications in the Public Domain or otherwise make
|
||||
them Freely Available, such as by posting said modifications to Usenet
|
||||
or an equivalent medium, or placing the modifications on a major archive
|
||||
site such as ftp.uu.net, or by allowing the Copyright Holder to include
|
||||
your modifications in the Standard Version of the Package.
|
||||
|
||||
c) Rename any non-standard executables so the names do not conflict
|
||||
with standard executables, which must also be provided, and provide
|
||||
a separate manual page (or equivalent) for each non-standard executable
|
||||
that clearly documents how it differs from the Standard Version.
|
||||
|
||||
d) Make other distribution arrangements with the Copyright Holder.
|
||||
|
||||
4. You may distribute the programs of this Package in object code or
|
||||
executable form, provided that you do at least ONE of the following:
|
||||
|
||||
a) Distribute a Standard Version of the executables and library
|
||||
files, together with instructions (in the manual page or
|
||||
equivalent) on where to get the Standard Version.
|
||||
|
||||
b) Accompany the distribution with the machine-readable source of
|
||||
the Package with your modifications.
|
||||
|
||||
c) Accompany any non-standard executables with their corresponding
|
||||
Standard Version executables, give the non-standard executables
|
||||
non-standard names, and clearly document the differences in manual
|
||||
pages (or equivalent), together with instructions on where to get
|
||||
the Standard Version.
|
||||
|
||||
d) Make other distribution arrangements with the Copyright Holder.
|
||||
|
||||
5. You may charge a reasonable copying fee for any distribution of
|
||||
this Package. You may charge any fee you choose for support of this
|
||||
Package. You may not charge a fee for this Package itself. However,
|
||||
you may distribute this Package in aggregate with other (possibly
|
||||
commercial) programs as part of a larger (possibly commercial)
|
||||
software distribution provided that you do not advertise this Package
|
||||
as a product of your own.
|
||||
|
||||
6. The scripts and library files supplied as input to or produced as
|
||||
output from the programs of this Package do not automatically fall
|
||||
under the copyright of this Package, but belong to whomever generated
|
||||
them, and may be sold commercially, and may be aggregated with this
|
||||
Package.
|
||||
|
||||
7. Scripts, suites, or programs supplied by you that depend on or
|
||||
otherwise make use of this Package shall not be considered part of
|
||||
this Package.
|
||||
|
||||
8. The name of the Copyright Holder may not be used to endorse or
|
||||
promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
The End
|
||||
|
||||
|
||||
http://www.spinwardstars.com/frontier/fal.html
|
||||
283
src/portable_snprintf/README
Normal file
283
src/portable_snprintf/README
Normal file
@@ -0,0 +1,283 @@
|
||||
|
||||
snprintf.c
|
||||
- a portable implementation of snprintf,
|
||||
including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
|
||||
|
||||
snprintf is a routine to convert numeric and string arguments to
|
||||
formatted strings. It is similar to sprintf(3) provided in a system's
|
||||
C library, yet it requires an additional argument - the buffer size -
|
||||
and it guarantees never to store anything beyond the given buffer,
|
||||
regardless of the format or arguments to be formatted. Some newer
|
||||
operating systems do provide snprintf in their C library, but many do
|
||||
not or do provide an inadequate (slow or idiosyncratic) version, which
|
||||
calls for a portable implementation of this routine.
|
||||
|
||||
Author
|
||||
|
||||
Mark Martinec <mark.martinec@ijs.si>, April 1999, June 2000
|
||||
Copyright <20> 1999, Mark Martinec
|
||||
|
||||
Terms and conditions ...
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the Frontier Artistic License which comes with
|
||||
this Kit.
|
||||
|
||||
Features
|
||||
|
||||
* careful adherence to specs regarding flags, field width and
|
||||
precision;
|
||||
* good performance for large string handling (large format, large
|
||||
argument or large paddings). Performance is similar to system's
|
||||
sprintf and in several cases significantly better (make sure you
|
||||
compile with optimizations turned on, tell the compiler the code
|
||||
is strict ANSI if necessary to give it more freedom for
|
||||
optimizations);
|
||||
* return value semantics per ISO/IEC 9899:1999 ("ISO C99");
|
||||
* written in standard ISO/ANSI C - requires an ANSI C compiler.
|
||||
|
||||
Supported conversion specifiers and data types
|
||||
|
||||
This snprintf only supports the following conversion specifiers: s, c,
|
||||
d, o, u, x, X, p (and synonyms: i, D, U, O - see below) with flags:
|
||||
'-', '+', ' ', '0' and '#'. An asterisk is supported for field width
|
||||
as well as precision.
|
||||
|
||||
Length modifiers 'h' (short int), 'l' (long int), and 'll' (long long
|
||||
int) are supported.
|
||||
|
||||
NOTE:
|
||||
|
||||
If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
|
||||
length modifier 'll' is recognized but treated the same as 'l',
|
||||
which may cause argument value truncation! Defining
|
||||
SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
|
||||
handles length modifier 'll'. long long int is a language extension
|
||||
which may not be portable.
|
||||
|
||||
Conversion of numeric data (conversion specifiers d, o, u, x, X, p)
|
||||
with length modifiers (none or h, l, ll) is left to the system routine
|
||||
sprintf, but all handling of flags, field width and precision as well
|
||||
as c and s conversions is done very carefully by this portable
|
||||
routine. If a string precision (truncation) is specified (e.g. %.8s)
|
||||
it is guaranteed the string beyond the specified precision will not be
|
||||
referenced.
|
||||
|
||||
Length modifiers h, l and ll are ignored for c and s conversions (data
|
||||
types wint_t and wchar_t are not supported).
|
||||
|
||||
The following common synonyms for conversion characters are supported:
|
||||
* i is a synonym for d
|
||||
* D is a synonym for ld, explicit length modifiers are ignored
|
||||
* U is a synonym for lu, explicit length modifiers are ignored
|
||||
* O is a synonym for lo, explicit length modifiers are ignored
|
||||
|
||||
The D, O and U conversion characters are nonstandard, they are
|
||||
supported for backward compatibility only, and should not be used for
|
||||
new code.
|
||||
|
||||
The following is specifically not supported:
|
||||
* flag ' (thousands' grouping character) is recognized but ignored
|
||||
* numeric conversion specifiers: f, e, E, g, G and synonym F, as
|
||||
well as the new a and A conversion specifiers
|
||||
* length modifier 'L' (long double) and 'q' (quad - use 'll'
|
||||
instead)
|
||||
* wide character/string conversions: lc, ls, and nonstandard
|
||||
synonyms C and S
|
||||
* writeback of converted string length: conversion character n
|
||||
* the n$ specification for direct reference to n-th argument
|
||||
* locales
|
||||
|
||||
It is permitted for str_m to be zero, and it is permitted to specify
|
||||
NULL pointer for resulting string argument if str_m is zero (as per
|
||||
ISO C99).
|
||||
|
||||
The return value is the number of characters which would be generated
|
||||
for the given input, excluding the trailing null. If this value is
|
||||
greater or equal to str_m, not all characters from the result have
|
||||
been stored in str, output bytes beyond the (str_m-1) -th character
|
||||
are discarded. If str_m is greater than zero it is guaranteed the
|
||||
resulting string will be null-terminated.
|
||||
|
||||
NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
|
||||
but is different from some older and vendor implementations, and is
|
||||
also different from XPG, XSH5, SUSv2 specifications. For historical
|
||||
discussion on changes in the semantics and standards of snprintf see
|
||||
printf(3) man page in the Linux programmers manual.
|
||||
|
||||
Routines asprintf and vasprintf return a pointer (in the ptr argument)
|
||||
to a buffer sufficiently large to hold the resulting string. This
|
||||
pointer should be passed to free(3) to release the allocated storage
|
||||
when it is no longer needed. If sufficient space cannot be allocated,
|
||||
these functions will return -1 and set ptr to be a NULL pointer. These
|
||||
two routines are a GNU C library extensions (glibc).
|
||||
|
||||
Routines asnprintf and vasnprintf are similar to asprintf and
|
||||
vasprintf, yet, like snprintf and vsnprintf counterparts, will write
|
||||
at most str_m-1 characters into the allocated output string, the last
|
||||
character in the allocated buffer then gets the terminating null. If
|
||||
the formatted string length (the return value) is greater than or
|
||||
equal to the str_m argument, the resulting string was truncated and
|
||||
some of the formatted characters were discarded. These routines
|
||||
present a handy way to limit the amount of allocated memory to some
|
||||
sane value.
|
||||
|
||||
Availability
|
||||
|
||||
http://www.ijs.si/software/snprintf/
|
||||
* snprintf_1.3.tar.gz (1999-06-30), md5 sum: snprintf_1.3.tar.gz.md5
|
||||
* snprintf_2.1.tar.gz (2000-07-14), md5 sum: snprintf_2.1.tar.gz.md5
|
||||
* snprintf_2.2.tar.gz (2000-10-18), md5 sum: snprintf_2.2.tar.gz.md5
|
||||
|
||||
Mailing list
|
||||
|
||||
There is a very low-traffic mailing list snprintf-announce@ijs.si
|
||||
where announcements about new versions will be posted as well as
|
||||
warnings about threatening bugs if discovered. The posting is
|
||||
restricted to snprintf developer(s).
|
||||
|
||||
To subscribe to (or unsubscribe from) the mailing list please visit
|
||||
the list server's web page
|
||||
http://mailman.ijs.si/listinfo/snprintf-announce
|
||||
|
||||
You can also subscribe to the list by mailing the command SUBSCRIBE
|
||||
either in the subject or in the message body to the address
|
||||
snprintf-announce-request@ijs.si . You will be asked for confirmation
|
||||
before subscription will be effective.
|
||||
|
||||
The list of members is only accessible to the list administrator, so
|
||||
there is no need for concern about automatic e-mail address gatherers.
|
||||
|
||||
Questions about the mailing list and concerns for the attention of a
|
||||
person should be sent to snprintf-announce-admin@ijs.si
|
||||
|
||||
There is no general discussion list about portable snprintf at the
|
||||
moment. Please send comments and suggestion to the author.
|
||||
|
||||
Revision history
|
||||
|
||||
Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade.
|
||||
|
||||
1999-06-30 V1.3 Mark Martinec <mark.martinec@ijs.si>
|
||||
|
||||
+ fixed runaway loop (eventually crashing when str_l wraps
|
||||
beyond 2^31) while copying format string without conversion
|
||||
specifiers to a buffer that is too short (thanks to Edwin
|
||||
Young <edwiny@autonomy.com> for spotting the problem);
|
||||
+ added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to
|
||||
snprintf.h
|
||||
|
||||
2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec@ijs.si>
|
||||
|
||||
+ relaxed license terms: The Artistic License now applies. You
|
||||
may still apply the GNU GENERAL PUBLIC LICENSE as was
|
||||
distributed with previous versions, if you prefer;
|
||||
+ changed REVISION HISTORY dates to use ISO 8601 date format;
|
||||
+ added vsnprintf (patch also independently proposed by Caol<6F>n
|
||||
McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
|
||||
|
||||
2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si>
|
||||
|
||||
+ removed POSIX check for str_m < 1; value 0 for str_m is
|
||||
allowed by ISO C99 (and GNU C library 2.1) (pointed out on
|
||||
2000-05-04 by Caol<6F>n McNamara, caolan@ csn dot ul dot ie).
|
||||
Besides relaxed license this change in standards adherence is
|
||||
the main reason to bump up the major version number;
|
||||
+ added nonstandard routines asnprintf, vasnprintf, asprintf,
|
||||
vasprintf that dynamically allocate storage for the resulting
|
||||
string; these routines are not compiled by default, see
|
||||
comments where NEED_V?ASN?PRINTF macros are defined;
|
||||
+ autoconf contributed by Caol<6F>n McNamara
|
||||
|
||||
2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si>
|
||||
|
||||
+ BUG FIX: the %c conversion used a temporary variable that was
|
||||
no longer in scope when referenced, possibly causing
|
||||
incorrect resulting character;
|
||||
+ BUG FIX: make precision and minimal field width unsigned to
|
||||
handle huge values (2^31 <= n < 2^32) correctly; also be more
|
||||
careful in the use of signed/unsigned/size_t internal
|
||||
variables -- probably more careful than many vendor
|
||||
implementations, but there may still be a case where huge
|
||||
values of str_m, precision or minimal field could cause
|
||||
incorrect behaviour;
|
||||
+ use separate variables for signed/unsigned arguments, and for
|
||||
short/int, long, and long long argument lengths to avoid
|
||||
possible incompatibilities on certain computer architectures.
|
||||
Also use separate variable arg_sign to hold sign of a numeric
|
||||
argument, to make code more transparent;
|
||||
+ some fiddling with zero padding and "0x" to make it Linux
|
||||
compatible;
|
||||
+ systematically use macros fast_memcpy and fast_memset instead
|
||||
of case-by-case hand optimization; determine some breakeven
|
||||
string lengths for different architectures;
|
||||
+ terminology change: format -> conversion specifier, C9x ->
|
||||
ISO/IEC 9899:1999 ("ISO C99"), alternative form -> alternate
|
||||
form, data type modifier -> length modifier;
|
||||
+ several comments rephrased and new ones added;
|
||||
+ make compiler not complain about 'credits' defined but not
|
||||
used;
|
||||
|
||||
Other implementations of snprintf
|
||||
|
||||
I am aware of some other (more or less) portable implementations of
|
||||
snprintf. I do not claim they are free software - please refer to
|
||||
their respective copyright and licensing terms. If you know of other
|
||||
versions please let me know.
|
||||
* a very thorough implementation (src/util_snprintf.c) by the Apache
|
||||
Group distributed with the Apache web server -
|
||||
http://www.apache.org/ . Does its own floating point conversions
|
||||
using routines ecvt(3), fcvt(3) and gcvt(3) from the standard C
|
||||
library or from the GNU libc.
|
||||
This is from the code:
|
||||
|
||||
This software [...] was originally based on public domain software
|
||||
written at the National Center for Supercomputing Applications,
|
||||
University of Illinois, Urbana-Champaign.
|
||||
[...] This code is based on, and used with the permission of, the
|
||||
SIO stdio-replacement strx_* functions by Panos Tsirigotis
|
||||
<panos@alumni.cs.colorado.edu> for xinetd.
|
||||
* QCI Utilities use a modified version of snprintf from the Apache
|
||||
group.
|
||||
* implementations as distributed with OpenBSD, FreeBSD, and NetBSD
|
||||
are all wrappers to vfprintf.c, which is derived from software
|
||||
contributed to Berkeley by Chris Torek.
|
||||
* implementation from Prof. Patrick Powell <papowell@sdsu.edu>,
|
||||
Dept. Electrical and Computer Engineering, San Diego State
|
||||
University, San Diego, CA 92182-1309, published in Bugtraq
|
||||
archives for 3rd quarter (Jul-Aug) 1995. No floating point
|
||||
conversions.
|
||||
* Brandon Long's <blong@fiction.net> modified version of Prof.
|
||||
Patrick Powell's snprintf with contributions from others. With
|
||||
minimal floating point support.
|
||||
* implementation (src/snprintf.c) as distributed with sendmail -
|
||||
http://www.sendmail.org/ is a cleaned up Prof. Patrick Powell's
|
||||
version to compile properly and to support .precision and %lx.
|
||||
* implementation from Caol<6F>n McNamara available at
|
||||
http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz, handles
|
||||
floating point.
|
||||
* implementation used by newlog (a replacement for syslog(3)) made
|
||||
available by the SOS Corporation. Enabling floating point support
|
||||
is a compile-time option.
|
||||
* implementation by Michael Richardson <mcr@metis.milkyway.com> is
|
||||
available at http://sandelman.ottawa.on.ca/SSW/snp/snp.html. It is
|
||||
based on BSD44-lite's vfprintf() call, modified to function on
|
||||
SunOS. Needs internal routines from the 4.4 strtod (included),
|
||||
requires GCC to compile the long long (aka quad_t) portions.
|
||||
* implementation from Tomi Salo <ttsalo@ssh.fi> distributed with SSH
|
||||
2.0 Unix Server. Not in public domain. Floating point conversions
|
||||
done by system's sprintf.
|
||||
* and for completeness: my portable version described in this very
|
||||
document available at http://www.ijs.si/software/snprintf/ .
|
||||
|
||||
In retrospect, it appears that a lot of effort was wasted by many
|
||||
people for not being aware of what others are doing. Sigh.
|
||||
|
||||
Also of interest: The Approved Base Working Group Resolution for XSH5,
|
||||
Ref: bwg98-006, Topic: snprintf.
|
||||
_________________________________________________________________
|
||||
|
||||
mm
|
||||
Last updated: 2000-10-18
|
||||
|
||||
Valid HTML 4.0!
|
||||
382
src/portable_snprintf/README.html
Normal file
382
src/portable_snprintf/README.html
Normal file
@@ -0,0 +1,382 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link rev="made" href="mailto:mark.martinec@ijs.si">
|
||||
<title>
|
||||
snprintf.c - a portable implementation of snprintf
|
||||
(including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf)
|
||||
</title>
|
||||
<meta http-equiv="Content-Language" content="en">
|
||||
<meta name="author" content="Mark Martinec">
|
||||
<meta name="copyright" content="Copyright 2000 Mark Martinec, All Rights Reserved">
|
||||
<meta name="date" content="2000-10-18">
|
||||
<meta name="keywords" lang="en"
|
||||
content="snprintf,portable,vsnprintf,asnprintf,vasnprintf,asprintf,vasprintf
|
||||
ISO/IEC 9899:1999,ISO C99,ISO C9x,POSIX">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
body { background: white; color: black }
|
||||
-->
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><b>snprintf.c</b>
|
||||
<br> - a portable implementation of snprintf,
|
||||
<br><font size="+1">including
|
||||
vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf</font>
|
||||
</h1>
|
||||
|
||||
<p><b>snprintf</b> is a routine to convert numeric and string arguments
|
||||
to formatted strings. It is similar to sprintf(3) provided in a
|
||||
system's C library, yet it requires an additional argument - the buffer
|
||||
size - and it guarantees never to store anything beyond the given buffer,
|
||||
regardless of the format or arguments to be formatted. Some newer
|
||||
operating systems do provide <b>snprintf</b> in their C library,
|
||||
but many do not or do provide an inadequate (slow or idiosyncratic)
|
||||
version, which calls for a portable implementation of this routine.
|
||||
|
||||
<h2>Author</h2>
|
||||
|
||||
<p><a href="http://www.ijs.si/people/mark/">Mark Martinec</a>
|
||||
<<a href="mailto:mark.martinec@ijs.si">mark.martinec@ijs.si</a>>,
|
||||
April 1999, June 2000
|
||||
<br>Copyright © 1999, Mark Martinec
|
||||
|
||||
<h2>Terms and conditions ...</h2>
|
||||
|
||||
<p>This program is free software; you can redistribute it
|
||||
and/or modify it under the terms of the
|
||||
<i><a href="./LICENSE.txt">Frontier Artistic License</a></i>
|
||||
which comes with this Kit.
|
||||
|
||||
<h2>Features</h2>
|
||||
|
||||
<ul>
|
||||
<li>careful adherence to specs regarding flags, field width and precision;
|
||||
<li>good performance for large string handling (large format, large argument
|
||||
or large paddings). Performance is similar to system's <b>sprintf</b>
|
||||
and in several cases significantly better (make sure you compile with
|
||||
optimizations turned on, tell the compiler the code is strict ANSI
|
||||
if necessary to give it more freedom for optimizations);
|
||||
<li>return value semantics per ISO/IEC 9899:1999 ("ISO C99");
|
||||
<li>written in standard ISO/ANSI C - requires an ANSI C compiler.
|
||||
</ul>
|
||||
|
||||
<h2>Supported conversion specifiers and data types</h2>
|
||||
|
||||
<p>This <b>snprintf</b> only supports the following conversion specifiers:
|
||||
s, c, d, o, u, x, X, p (and synonyms: i, D, U, O - see below)
|
||||
with flags: '-', '+', ' ', '0' and '#'.
|
||||
An asterisk is supported for field width as well as precision.
|
||||
|
||||
<p>Length modifiers 'h' (<i>short int</i>), 'l' (<i>long int</i>),
|
||||
and 'll' (<i>long long int</i>) are supported.
|
||||
|
||||
<p>NOTE:
|
||||
<blockquote>
|
||||
If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default)
|
||||
the length modifier 'll' is recognized but treated the same as 'l',
|
||||
which may cause argument value truncation!
|
||||
Defining SNPRINTF_LONGLONG_SUPPORT requires that your system's
|
||||
<b>sprintf</b> also handles length modifier 'll'.
|
||||
<i>long long int</i> is a language extension which may not be portable.
|
||||
</blockquote>
|
||||
|
||||
<p>Conversion of numeric data (conversion specifiers d, o, u, x, X, p)
|
||||
with length modifiers (none or h, l, ll) is left to the system
|
||||
routine <b>sprintf</b>, but all handling of flags, field width and precision
|
||||
as well as c and s conversions is done very carefully by this portable routine.
|
||||
If a string precision (truncation) is specified (e.g. %.8s) it is
|
||||
guaranteed the string beyond the specified precision will not be referenced.
|
||||
|
||||
<p>Length modifiers h, l and ll are ignored for c and s conversions
|
||||
(data types <i>wint_t</i> and <i>wchar_t</i> are not supported).
|
||||
|
||||
<p>The following common synonyms for conversion characters are supported:
|
||||
<ul>
|
||||
<li>i is a synonym for d
|
||||
<li>D is a synonym for ld, explicit length modifiers are ignored
|
||||
<li>U is a synonym for lu, explicit length modifiers are ignored
|
||||
<li>O is a synonym for lo, explicit length modifiers are ignored
|
||||
</ul>
|
||||
The D, O and U conversion characters are nonstandard, they are supported
|
||||
for backward compatibility only, and should not be used for new code.
|
||||
|
||||
<p>The following is specifically <b>not</b> supported:
|
||||
<ul>
|
||||
<li>flag ' (thousands' grouping character) is recognized but ignored
|
||||
<li>numeric conversion specifiers: f, e, E, g, G and synonym F,
|
||||
as well as the new a and A conversion specifiers
|
||||
<li>length modifier 'L' (<i>long double</i>)
|
||||
and 'q' (<i>quad</i> - use 'll' instead)
|
||||
<li>wide character/string conversions: lc, ls, and nonstandard
|
||||
synonyms C and S
|
||||
<li>writeback of converted string length: conversion character n
|
||||
<li>the n$ specification for direct reference to n-th argument
|
||||
<li>locales
|
||||
</ul>
|
||||
|
||||
<p>It is permitted for str_m to be zero, and it is permitted to specify NULL
|
||||
pointer for resulting string argument if str_m is zero (as per ISO C99).
|
||||
|
||||
<p>The return value is the number of characters which would be generated
|
||||
for the given input, <i>excluding</i> the trailing null. If this value
|
||||
is greater or equal to str_m, not all characters from the result
|
||||
have been stored in str, output bytes beyond the (str_m-1) -th character
|
||||
are discarded. If str_m is greater than zero it is guaranteed
|
||||
the resulting string will be null-terminated.
|
||||
|
||||
<p>NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
|
||||
but is different from some older and vendor implementations,
|
||||
and is also different from XPG, XSH5, SUSv2 specifications.
|
||||
For historical discussion on changes in the semantics and standards
|
||||
of snprintf see printf(3) man page in the Linux programmers manual.
|
||||
|
||||
<p>Routines asprintf and vasprintf return a pointer (in the ptr argument)
|
||||
to a buffer sufficiently large to hold the resulting string. This pointer
|
||||
should be passed to free(3) to release the allocated storage when it is
|
||||
no longer needed. If sufficient space cannot be allocated, these functions
|
||||
will return -1 and set ptr to be a NULL pointer. These two routines are a
|
||||
GNU C library extensions (glibc).
|
||||
|
||||
<p>Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
|
||||
yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
|
||||
characters into the allocated output string, the last character in the
|
||||
allocated buffer then gets the terminating null. If the formatted string
|
||||
length (the return value) is greater than or equal to the str_m argument,
|
||||
the resulting string was truncated and some of the formatted characters
|
||||
were discarded. These routines present a handy way to limit the amount
|
||||
of allocated memory to some sane value.
|
||||
|
||||
<h2>Availability</h2>
|
||||
|
||||
<p><a href="http://www.ijs.si/software/snprintf/"
|
||||
>http://www.ijs.si/software/snprintf/</a>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="./snprintf_1.3.tar.gz">snprintf_1.3.tar.gz</a> (1999-06-30),
|
||||
md5 sum: <a href="./snprintf_1.3.tar.gz.md5">snprintf_1.3.tar.gz.md5</a>
|
||||
|
||||
<li>
|
||||
<a href="./snprintf_2.1.tar.gz">snprintf_2.1.tar.gz</a> (2000-07-14),
|
||||
md5 sum: <a href="./snprintf_2.1.tar.gz.md5">snprintf_2.1.tar.gz.md5</a>
|
||||
|
||||
<li>
|
||||
<a href="./snprintf_2.2.tar.gz">snprintf_2.2.tar.gz</a> (2000-10-18),
|
||||
md5 sum: <a href="./snprintf_2.2.tar.gz.md5">snprintf_2.2.tar.gz.md5</a>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Mailing list</h2>
|
||||
|
||||
<p>There is a very low-traffic mailing list <i>snprintf-announce@ijs.si</i>
|
||||
where announcements about new versions will be posted
|
||||
as well as warnings about threatening bugs if discovered.
|
||||
The posting is restricted to snprintf developer(s).
|
||||
|
||||
<p>To subscribe to (or unsubscribe from) the mailing list
|
||||
please visit the list server's web page
|
||||
<a href="http://mailman.ijs.si/listinfo/snprintf-announce"
|
||||
>http://mailman.ijs.si/listinfo/snprintf-announce</a>
|
||||
|
||||
<p>You can also subscribe to the list by mailing
|
||||
the command SUBSCRIBE either in the subject or in the message body
|
||||
to the address <a href="mailto:snprintf-announce-request@ijs.si"
|
||||
>snprintf-announce-request@ijs.si</a> . You will be asked for
|
||||
confirmation before subscription will be effective.
|
||||
|
||||
<p>The list of members is only accessible to the list administrator,
|
||||
so there is no need for concern about automatic e-mail address gatherers.
|
||||
|
||||
<p>Questions about the mailing list and concerns for the attention
|
||||
of a person should be sent to <a href="mailto:snprintf-announce-admin@ijs.si"
|
||||
>snprintf-announce-admin@ijs.si</a>
|
||||
|
||||
<p>There is no <i>general</i> discussion list about portable snprintf
|
||||
at the moment. Please send comments and suggestion to the author.
|
||||
|
||||
|
||||
<h2>Revision history</h2>
|
||||
|
||||
<p><b>Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade.</b>
|
||||
|
||||
<dl>
|
||||
<dt>1999-06-30 V1.3 Mark Martinec <mark.martinec@ijs.si>
|
||||
<dd><ul>
|
||||
<li>fixed runaway loop (eventually crashing when str_l wraps
|
||||
beyond 2^31) while copying format string without
|
||||
conversion specifiers to a buffer that is too short
|
||||
(thanks to Edwin Young <edwiny@autonomy.com> for spotting the problem);
|
||||
<li>added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to snprintf.h
|
||||
</ul>
|
||||
|
||||
<dt>2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec@ijs.si>
|
||||
<dd><ul>
|
||||
<li>relaxed license terms:
|
||||
<a href="./LICENSE.txt">The Artistic License</a> now applies.
|
||||
You may still apply the GNU GENERAL PUBLIC LICENSE
|
||||
as was distributed with previous versions, if you prefer;
|
||||
<li>changed REVISION HISTORY dates to use
|
||||
<a href="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">ISO 8601
|
||||
date format</a>;
|
||||
<li>added vsnprintf (patch also independently proposed by
|
||||
Caolán McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
|
||||
</ul>
|
||||
|
||||
<dt>2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si>
|
||||
<dd><ul>
|
||||
<li>removed POSIX check for str_m < 1; value 0 for str_m is
|
||||
allowed by ISO C99 (and GNU C library 2.1) (pointed out
|
||||
on 2000-05-04 by Caolán McNamara, caolan@ csn dot ul dot ie).
|
||||
Besides relaxed license this change in standards adherence
|
||||
is the main reason to bump up the major version number;
|
||||
<li>added nonstandard routines asnprintf, vasnprintf, asprintf,
|
||||
vasprintf that dynamically allocate storage for the
|
||||
resulting string; these routines are not compiled by default,
|
||||
see comments where NEED_V?ASN?PRINTF macros are defined;
|
||||
<li>autoconf contributed by Caolán McNamara
|
||||
</ul>
|
||||
|
||||
<dt>2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si>
|
||||
<dd><ul>
|
||||
<li><b>BUG FIX</b>: the %c conversion used a temporary variable
|
||||
that was no longer in scope when referenced,
|
||||
possibly causing incorrect resulting character;
|
||||
<li>BUG FIX: make precision and minimal field width unsigned
|
||||
to handle huge values (2^31 <= n < 2^32) correctly;
|
||||
also be more careful in the use of signed/unsigned/size_t
|
||||
internal variables -- probably more careful than many
|
||||
vendor implementations, but there may still be a case
|
||||
where huge values of str_m, precision or minimal field
|
||||
could cause incorrect behaviour;
|
||||
<li>use separate variables for signed/unsigned arguments,
|
||||
and for short/int, long, and long long argument lengths
|
||||
to avoid possible incompatibilities on certain
|
||||
computer architectures. Also use separate variable
|
||||
arg_sign to hold sign of a numeric argument,
|
||||
to make code more transparent;
|
||||
<li>some fiddling with zero padding and "0x" to make it
|
||||
Linux compatible;
|
||||
<li>systematically use macros fast_memcpy and fast_memset
|
||||
instead of case-by-case hand optimization; determine some
|
||||
breakeven string lengths for different architectures;
|
||||
<li>terminology change: <i>format</i> -> <i>conversion specifier</i>,
|
||||
<i>C9x</i> -> <i>ISO/IEC 9899:1999 ("ISO C99")</i>,
|
||||
<i>alternative form</i> -> <i>alternate form</i>,
|
||||
<i>data type modifier</i> -> <i>length modifier</i>;
|
||||
<li>several comments rephrased and new ones added;
|
||||
<li>make compiler not complain about 'credits' defined but
|
||||
not used;
|
||||
</ul>
|
||||
</dl>
|
||||
|
||||
<h2>Other implementations of snprintf</h2>
|
||||
|
||||
<p>I am aware of some other (more or less) portable implementations
|
||||
of <b>snprintf</b>. I do not claim they are free software - please refer
|
||||
to their respective copyright and licensing terms.
|
||||
If you know of other versions please let
|
||||
<a href="http://www.ijs.si/people/mark/">me</a> know.
|
||||
|
||||
<ul>
|
||||
<li>a very thorough implementation (src/util_snprintf.c)
|
||||
by the Apache Group distributed with the
|
||||
<a href="http://www.apache.org/">Apache web server
|
||||
- http://www.apache.org/</a> .
|
||||
Does its own floating point conversions using routines
|
||||
ecvt(3), fcvt(3) and gcvt(3) from the standard C library
|
||||
or from the GNU libc.
|
||||
|
||||
<br>This is from the code:
|
||||
<blockquote>
|
||||
This software [...] was originally based
|
||||
on public domain software written at the
|
||||
<a href="http://www.ncsa.uiuc.edu/ncsa.html">National Center
|
||||
for Supercomputing Applications</a>, University of Illinois,
|
||||
Urbana-Champaign.<br>
|
||||
[...] This code is based on, and used with the permission of,
|
||||
the SIO stdio-replacement strx_* functions by Panos Tsirigotis
|
||||
<<a href="mailto:panos@alumni.cs.colorado.edu">panos@alumni.cs.colorado.edu</a>> for xinetd.
|
||||
</blockquote>
|
||||
|
||||
<li><a href="http://www.qlue.com/downloads/c_utils_README.html">QCI
|
||||
Utilities</a> use a modified version of snprintf from the Apache group.
|
||||
|
||||
<li>implementations as distributed with
|
||||
<a href="http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/">OpenBSD</a>,
|
||||
<a href="http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/">FreeBSD</a>, and
|
||||
<a href="http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/basesrc/lib/libc/stdio/">NetBSD</a>
|
||||
are all wrappers to vfprintf.c, which is derived from software
|
||||
contributed to Berkeley by Chris Torek.
|
||||
|
||||
<li>implementation from Prof. Patrick Powell
|
||||
<<a href="mailto:papowell@sdsu.edu">papowell@sdsu.edu</a>>,
|
||||
Dept. Electrical and Computer Engineering, San Diego State University,
|
||||
San Diego, CA 92182-1309, published in
|
||||
<a href="http://www.geek-girl.com/bugtraq/1995_3/0217.html">Bugtraq
|
||||
archives for 3rd quarter (Jul-Aug) 1995</a>.
|
||||
No floating point conversions.
|
||||
|
||||
<li>Brandon Long's
|
||||
<<a href="mailto:blong@fiction.net">blong@fiction.net</a>>
|
||||
<a href="http://www.fiction.net/~blong/programs/">modified version</a>
|
||||
of Prof. Patrick Powell's snprintf with contributions from others.
|
||||
With minimal floating point support.
|
||||
|
||||
<li>implementation (src/snprintf.c) as distributed with
|
||||
<a href="http://www.sendmail.org/">sendmail - http://www.sendmail.org/</a>
|
||||
is a cleaned up Prof. Patrick Powell's version
|
||||
to compile properly and to support .precision and %lx.
|
||||
|
||||
<li>implementation from <a href="http://www.csn.ul.ie/~caolan/"
|
||||
>Caolán McNamara</a> available at
|
||||
<a href="http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz"
|
||||
>http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz</a>,
|
||||
handles floating point.
|
||||
|
||||
<li>implementation used by
|
||||
<a href="ftp://ftp.soscorp.com/pub/sos/lib">newlog</a>
|
||||
(a replacement for syslog(3)) made available by
|
||||
the <a href="http://www.soscorp.com">SOS Corporation</a>.
|
||||
Enabling floating point support is a compile-time option.
|
||||
|
||||
<li>implementation by Michael Richardson
|
||||
<<a href="mailto:mcr@metis.milkyway.com">mcr@metis.milkyway.com</a>>
|
||||
is available at
|
||||
<a href="http://sandelman.ottawa.on.ca/SSW/snp/snp.html"
|
||||
>http://sandelman.ottawa.on.ca/SSW/snp/snp.html</a>.
|
||||
It is based on BSD44-lite's vfprintf() call, modified to function
|
||||
on SunOS. Needs internal routines from the 4.4 strtod (included),
|
||||
requires GCC to compile the long long (aka quad_t) portions.
|
||||
|
||||
<li>implementation from Tomi Salo
|
||||
<<a href="mailto:ttsalo@ssh.fi">ttsalo@ssh.fi</a>>
|
||||
distributed with
|
||||
<a href="http://www.Europe.DataFellows.com/f-secure/ssh/">SSH 2.0
|
||||
Unix Server</a>. Not in public domain.
|
||||
Floating point conversions done by system's sprintf.
|
||||
|
||||
<li>and for completeness: <a href="http://www.ijs.si/people/mark/">my</a>
|
||||
portable version described in this very document available at
|
||||
<a href="http://www.ijs.si/software/snprintf/"
|
||||
>http://www.ijs.si/software/snprintf/</a> .
|
||||
</ul>
|
||||
|
||||
In retrospect, it appears that a lot of effort was wasted by many
|
||||
people for not being aware of what others are doing. Sigh.
|
||||
|
||||
<p>Also of interest:
|
||||
<a href="http://www.opengroup.org/platform/resolutions/bwg98-006.html"
|
||||
>The Approved Base Working Group Resolution for XSH5,
|
||||
Ref: bwg98-006, Topic: snprintf</a>.
|
||||
|
||||
<p><hr>
|
||||
<i><a href="http://www.ijs.si/people/mark/">mm</a></i>
|
||||
<br>Last updated: 2000-10-18
|
||||
|
||||
<p><a href="http://validator.w3.org/check/referer"
|
||||
><img src="/images/vh40.gif" alt="Valid HTML 4.0!"
|
||||
border="0" width="88" height="31"></a>
|
||||
</body>
|
||||
</html>
|
||||
1044
src/portable_snprintf/snprintf.c
Normal file
1044
src/portable_snprintf/snprintf.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
{
|
||||
global: MESA*runtime_log*;GIT_VERSION_*;
|
||||
global: MESA*runtime_log*;GIT_VERSION_*;MESA_shm_alloc*;MESA_handle_fmt_rule_register;MESA_shm_ring_queue*;MESA_shm_unlink;loglevel_to_name;
|
||||
local: *;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user