35 Commits

Author SHA1 Message Date
guo_peixu
451af3b929 支持过滤模块名,支持匹配通配符"*" 2022-07-08 10:07:30 +08:00
guo_peixu
3d6d7b909a 修改问题:日志中不显示模块名 2022-07-06 17:00:01 +08:00
guo_peixu
fac6124b6e 修改问题:消费者正在向共享内存中写入日志过滤规则时,可能恰好被生产者读取,导致生产者读取到脏数据 2022-07-06 12:05:45 +08:00
guo_peixu
1655537e0d 修改问题:由于日志长度入参错误,导致向缓冲区拷贝了超过实际长度的无效数据。
将环形缓冲区的操作以接口形式提供,减少与外部程序的耦合
2022-07-05 18:25:55 +08:00
guo_peixu
6bf1bc201c 修改问题:日志接口的handle在使用前未判空 2022-07-05 14:34:39 +08:00
guo_peixu
54a2f747d5 记录一些消费者在运行过程中的错误日志 2022-07-05 10:58:44 +08:00
guo_peixu
1898fb9eb3 修改问题:消费者临时存储进程号的链表,可能free不完全 2022-07-05 10:14:08 +08:00
guo_peixu
db8b5e259e 修改问题:当消费者不对某个进程进行消费时,该进程仍然会产生无效日志。
消费者将自己订阅的生产者进程号写入共享内存,生产者或消费者重启后,生产者读共享内存匹配一次自己的进程号,避免进行无效生产
2022-07-04 19:08:19 +08:00
guo_peixu
ba9dd722b2 修改生产者向共享内存写日志时,可能发生踩内存的问题
计算缓冲区可用长度时,忽略了日志等级占用的四字节,有踩内存风险
2022-07-04 14:11:04 +08:00
guo_peixu
09b22fd49a 支持过滤日志等级
消费者进程将日志等级要求写入共享内存,生产者读取共享,只产生符合日志等级要求的日志
2022-07-04 14:00:18 +08:00
guo_peixu
0fb5b8adea 修改日志不显示日志等级的问题 2022-07-01 19:01:10 +08:00
guo_peixu
5e17fced23 修改生产者收到退出信号后,可能不退出的问题 2022-06-30 15:26:23 +08:00
guo_peixu
8267c9712e 修改生产者和消费者都退出后,共享内存未删除的问题
生产者消费者注册信号处理函数,退出时检查如果没有其它进程使用共享内存,将共享内存删除
2022-06-30 15:16:08 +08:00
guo_peixu
89899d2d0a 修改多线程重复映射同一块共享内存,可能导致进程的线性地址耗尽问题。
一个线程映射共享内存后,将地址存储,其它线程可直接使用,不用重复映射
2022-06-30 11:19:40 +08:00
guo_peixu
a5bffd2fe7 消费者通过注册信号处理函数,在退出时向共享内存中添加状态标记,生产者通过此标记确定消费者是否在运行。删除信号量相关代码 2022-06-29 18:24:51 +08:00
guo_peixu
dab9b5c03d 使用进程间共享的信号量,标记消费者进程是否正在运行 2022-06-28 18:42:05 +08:00
guo_peixu
c059e7479e 修改日志文件的日期错误的问题。删除功能重复的无效代码。 2022-06-24 16:49:55 +08:00
guo_peixu
e13e857381 使用tty命令获取当前终端名称,将日志输出到当前终端 2022-06-23 17:12:25 +08:00
guo_peixu
fada178df2 修改线程获取缓冲区失败问题。
修改共享内存地址映射失败时的判错条件,shmat失败会返回(void *)-1而不是NULL
2022-06-23 14:20:32 +08:00
guo_peixu
4531d74c80 优化共享内存解析格式,减少内存浪费。优化生产者进程的效率。 2022-06-23 11:41:16 +08:00
guo_peixu
775982f2d5 修改日志丢失的问题。缓冲区未遍历完而退出循环,会导致刷新不及时而丢失日志 2022-06-22 19:05:39 +08:00
guo_peixu
7a88ab75ec 在进行snprintf之前,进行缓冲区的判满,提高处理效率。去掉无意义的goto语句 2022-06-22 18:42:59 +08:00
guo_peixu
b94706ac06 启动消费者进程时,支持设置消费指定生产者的日志,支持设置将日志输出到文件或者终端 2022-06-22 17:49:58 +08:00
guo_peixu
3421f97f95 支持输出日志到指定目录 2022-06-22 09:44:55 +08:00
guo_peixu
93c07a240b 使用共享锁来标记环形缓冲区是否正在被使用 2022-06-17 18:14:24 +08:00
guo_peixu
0ce7424fa1 消费者进程启动时,检查是否存在已启动的消费者进程,避免重复启动 2022-06-16 17:11:34 +08:00
guo_peixu
21b84aa885 支持多个生产者进程的场景。当生产者的某个线程抢占环形缓冲区时,使用进程间的信号量代替线程锁来保护共享资源 2022-06-14 16:16:53 +08:00
guo_peixu
30f2094627 修改MESA_log动态库链接可执行程序失败问题。cmake配置中隐藏了动态库中的函数符号导致链接失败,添加这些对外提供的函数符号。 2022-06-13 14:39:44 +08:00
guo_peixu
7faf202791 修改编译问题。并将开源snprintf与MESA_log编译到一起。 2022-06-13 13:00:57 +08:00
guo_peixu
af7c375634 打开开源snprintf和日志消费者进程的编译 2022-06-10 17:26:40 +08:00
guo_peixu
d7b64b4577 提交环形缓冲区添加代码 2022-06-10 16:14:32 +08:00
guo_peixu
65ed1c3a92 提交环形缓冲区相关修改 2022-06-10 16:13:11 +08:00
guo_peixu
08fd550822 添加开源snprintf的相关编译宏 2022-06-08 11:10:41 +08:00
guo_peixu
c0c6a97a13 删除无用代码 2022-06-08 10:54:00 +08:00
guo_peixu
20f3fb58b7 snprintf支持格式化结构体 2022-06-07 16:26:10 +08:00
18 changed files with 4454 additions and 101 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View 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

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

View 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>
&lt;<a href="mailto:mark.martinec@ijs.si">mark.martinec@ijs.si</a>&gt;,
April 1999, June 2000
<br>Copyright &copy; 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: '-', '+', '&nbsp;', '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 &lt;mark.martinec@ijs.si&gt;
<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 &lt;edwiny@autonomy.com&gt; 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 &lt;mark.martinec@ijs.si&gt;
<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&aacute;n McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
</ul>
<dt>2000-06-27 V2.1 Mark Martinec &lt;mark.martinec@ijs.si&gt;
<dd><ul>
<li>removed POSIX check for str_m &lt; 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&aacute;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&aacute;n McNamara
</ul>
<dt>2000-10-06 V2.2 Mark Martinec &lt;mark.martinec@ijs.si&gt;
<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 &lt;= n &lt; 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> -&gt; <i>conversion specifier</i>,
<i>C9x</i> -&gt; <i>ISO/IEC 9899:1999 ("ISO C99")</i>,
<i>alternative form</i> -&gt; <i>alternate form</i>,
<i>data type modifier</i> -&gt; <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
&lt;<a href="mailto:panos@alumni.cs.colorado.edu">panos@alumni.cs.colorado.edu</a>&gt; 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
&lt;<a href="mailto:papowell@sdsu.edu">papowell@sdsu.edu</a>&gt;,
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
&lt;<a href="mailto:blong@fiction.net">blong@fiction.net</a>&gt;
<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&aacute;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
&lt;<a href="mailto:mcr@metis.milkyway.com">mcr@metis.milkyway.com</a>&gt;
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
&lt;<a href="mailto:ttsalo@ssh.fi">ttsalo@ssh.fi</a>&gt;
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>

File diff suppressed because it is too large Load Diff

View File

@@ -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: *;
};