--- ./zabbix-2.0.7/src/libs/zbxexec/execute.c.orig 2013-08-17 11:03:51.300379910 -0400 +++ ./zabbix-2.0.7/src/libs/zbxexec/execute.c 2013-08-25 11:03:33.193028287 -0400 @@ -242,6 +242,128 @@ #endif /* _WINDOWS */ +#ifndef _WINDOWS +static char *zbx_dynamic_path_key = ".so,"; +/****************************************************************************** + * * + * Function: zbx_is_dynamic_link_command * + * * + * Purpose: determine if a command specifies a dynamic link library * + * In windows you don't really need something like this because * + * the performance counter definition mechanism gives you the * + * same level of efficiency. * + * * + * Parameters: command - [IN] command for execution * + * * + * Return value: 1 if command format is filename.so,symbolname * + * 0 otherwise. * + * * + * Author: George Carrette * + * * + ******************************************************************************/ +static int zbx_is_dynamic_link_command(const char *command) +{ + if (strstr(command, zbx_dynamic_path_key) != NULL) + { + return(1); + } + else + { + return(0); + } +} + +#include + +/****************************************************************************** + * * + * Function: zbx_execute_dynamic * + * * + * Purpose: this function calls a function from a dynamic link library * + * * + * Parameters: command - [IN] command for execution * + * buffer - [OUT] buffer for output, if NULL - ignored * + * error - [OUT] error string if function fails * + * max_error_len - [IN] length of error buffer * + * * + * Return value: SUCCEED if processed successfully, * + * FAIL if library and symbol cannot be loaded/found * + * otherwise whatever underying dynamic function might return * + * * + * Author: George Carrette * + * * + ******************************************************************************/ +int zbx_execute_dynamic(const char *command, char **buffer, char *error, size_t max_error_len, int timeout) +{ + size_t buf_size = PIPE_BUFFER_SIZE; + int ret = FAIL; + char pathname[1024]; + char symbol[1024]; + char *ptr, *nxt; + void *dlopen_handle,*dlsym_return; + int (*dlsym_fcn)(const char *,char *,size_t,char *,size_t); + ptr = strstr(command, zbx_dynamic_path_key); + size_t len, inclen; + inclen = strlen(zbx_dynamic_path_key); + len = MIN(sizeof(pathname), (ptr - command) + inclen); + zbx_strlcpy(pathname, command, len); + ptr += inclen; + nxt = strchr(ptr,' '); + if (nxt != NULL) + { + inclen = nxt - ptr + 1; + } + else + { + inclen = strlen(ptr) + 1; + } + len = MIN(sizeof(symbol), inclen); + zbx_strlcpy(symbol, ptr, len); + if (NULL != buffer) + { + *buffer = zbx_realloc(*buffer, buf_size); + **buffer = '\0'; + } + dlopen_handle = dlopen(pathname, RTLD_LAZY); + zabbix_log(LOG_LEVEL_DEBUG, "In zbx_execute_dynamic %s handle = %p", + pathname, + dlopen_handle); + if (dlopen_handle == NULL) + { + zbx_snprintf(error,max_error_len, + "%s: %s", dlerror(), pathname); + } + else + { + dlsym_return = dlsym(dlopen_handle, symbol); + zabbix_log(LOG_LEVEL_DEBUG, "In zbx_execute_dynamic %s symbol = %p", + symbol, + dlsym_return); + if (dlsym_return == NULL) + { + zbx_snprintf(error,max_error_len, + "%s: %s in %s", dlerror(), + symbol, pathname); + } + else + { + dlsym_fcn = (int (*)(const char *,char *,size_t,char *,size_t)) + dlsym_return; + ret = (*dlsym_fcn)(command, *buffer, buf_size, + error, + max_error_len); + } + } + if (SUCCEED != ret && NULL != buffer) + { + zbx_free(*buffer); + *buffer = NULL; + } + return(ret); +} + +#endif + /****************************************************************************** * * * Function: zbx_execute * @@ -279,6 +401,12 @@ *error = '\0'; +#ifndef _WINDOWS + if (zbx_is_dynamic_link_command(command)) + { + return(zbx_execute_dynamic(command, buffer, error, max_error_len, timeout)); + } +#endif if (NULL != buffer) { *buffer = zbx_realloc(*buffer, buf_size); @@ -442,8 +570,10 @@ zabbix_log(LOG_LEVEL_WARNING, "%s", error); if (SUCCEED != ret && NULL != buffer) - zbx_free(*buffer); - + { + zbx_free(*buffer); + *buffer = NULL; + } return ret; } --- /dev/null 2013-08-25 10:31:33.566786381 -0400 +++ zbx-dyn.h 2013-08-25 12:56:16.398565243 -0400 @@ -0,0 +1,33 @@ +/* name: zbx-dyn.h + author: George J. Carrette gjc@alum.mit.edu + +This describes an interface to a subset of the api from zabbix-2.0.7/src/libs +but it also could include functions that are either exclusively +in the agent or server. It isn't possible to call the existing +zabbix include files directly because the dynamic api is implemented +in a slightly different way. + +*/ + +#define SUCCEED 0 +#define FAIL -1 + +#define LOG_LEVEL_EMPTY 0 +#define LOG_LEVEL_CRIT 1 +#define LOG_LEVEL_ERR 2 +#define LOG_LEVEL_WARNING 3 +#define LOG_LEVEL_DEBUG 4 +#define LOG_LEVEL_INFORMATION 127 + +#define zbx_malloc(old, size) (*zbx_dyn_malloc2)(__FILE__, __LINE__, old, size) +#define zbx_realloc(src, size) (*zbx_dyn_realloc2)(__FILE__, __LINE__, src, size) +#define zbx_snprintf (*zbx_dyn_snprintf) +#define zabbix_log (*zbx_dyn_log) + +void *(*zbx_dyn_malloc2)(const char *filename, int line, void *old, size_t size); +void *(*zbx_dyn_realloc2)(const char *filename, int line, void *old, size_t size); +void (*zbx_dyn_log)(int, const char *, ...); +size_t (*zbx_dyn_snprintf)(char *, size_t, const char *, ...); + +int zbx_dyn_init(char *, size_t); + --- /dev/null 2013-08-25 10:31:33.566786381 -0400 +++ zbx-dyn.c 2013-08-25 12:51:23.333111995 -0400 @@ -0,0 +1,120 @@ +/* name: zbx-dyn.c + author: George J. Carrette gjc@alum.mit.edu + purpose: initialize callbacks into the zabbix agent from within + a shared library loaded by zbx_execute_dynamic. + + There are various ways we could change how zabbix is built + so that we could reference functions from src/libs. + (e.g. using --export-dynamic or putting the entire library into + a shared library). + Or we could modify the interface within zbx_execute_dynamic + to pass in a structure containing the most popular callbacks. + However, we might as well just use the dynamic linker api + to get what we need, when we need it, without bothering with + how zabbix itself is built. +*/ + +#include +#include +#include +#include +#include + +#include "zbx-dyn.h" + +void *(*zbx_dyn_malloc2)(const char *filename, int line, void *old, size_t size) = NULL; +void *(*zbx_dyn_realloc2)(const char *filename, int line, void *old, size_t size) = NULL; +void (*zbx_dyn_log)(int, const char *, ...) = NULL; +size_t (*zbx_dyn_snprintf)(char *, size_t, const char *, ...) = NULL; + +static void *return_null(void) +{ + return(NULL); +} + +static void return_void(void) +{ +} + +static int return_fail(void) +{ + return(FAIL); +} + +static int return_zero(void) +{ + return(0); +} + +static int zbx_init_flag = 0; +static int zbx_init_status = FAIL; + +static void zbx_set_failure(char *error_buffer, size_t error_buffer_len) +{ + char *msg = "zbx-dyn init failure"; + if (strlen(msg) < error_buffer_len) + { + strcpy(error_buffer, msg); + } +} + +static void setup_fcn(int *ret,void *dlopen_handle, void **fcn, + const char *sym, + void *default_fcn) +{ + void *dlsym_return; + *fcn = default_fcn; + if (dlopen_handle == NULL) + { + *ret = FAIL; + } + dlsym_return = dlsym(dlopen_handle, sym); + if (dlsym_return == NULL) + { + *ret = FAIL; + } + else + { + *fcn = dlsym_return; + } +} + +int zbx_dyn_init(char *error_buffer, size_t error_buffer_len) +{ + if (zbx_init_flag == 1) + { + if (zbx_init_status != SUCCEED) + { + zbx_set_failure(error_buffer, error_buffer_len); + } + return(zbx_init_status); + } + void *dlopen_handle; + int ret = SUCCEED; + dlopen_handle = dlopen(NULL, RTLD_LAZY); + setup_fcn(&ret, + dlopen_handle, + (void **) &zbx_dyn_log, + "__zbx_zabbix_log", + (void *) &return_void); + zabbix_log(LOG_LEVEL_DEBUG, "In zbx_dyn_init handle = %p", dlopen_handle); + setup_fcn(&ret, + dlopen_handle, + (void **) &zbx_dyn_snprintf, + "__zbx_zbx_snprintf", + (void *) &return_zero); + setup_fcn(&ret, + dlopen_handle, + (void **) &zbx_dyn_malloc2, + "zbx_malloc2", + (void *) &return_null); + setup_fcn(&ret, + dlopen_handle, + (void **) &zbx_dyn_realloc2, + "zbx_realloc2", + (void *) &return_null); + zbx_init_status = ret; + zbx_init_flag = 1; + return(ret); +} + --- /dev/null 2013-08-25 10:31:33.566786381 -0400 +++ zbx-test.c 2013-08-25 12:52:27.165428519 -0400 @@ -0,0 +1,50 @@ +/* name: zbx-test.c + author: George J. Carrette gjc@alum.mit.edu + purpose: test the simple shared library interface + from zbx_execute. +*/ + +#include +#include +#include +#include +#include + +#include "zbx-dyn.h" + +static char ** null_ptr = NULL; + +int zbx_test_1(const char *command, + char *output_buffer, + size_t output_buffer_len, + char *error_buffer, + size_t error_buffer_len) { + char *buffer; + if (zbx_dyn_init(error_buffer, error_buffer_len) != SUCCEED) + { + return(FAIL); + } + zabbix_log(LOG_LEVEL_DEBUG,"Inside zbx_test_1 buflen = %d errbuflen = %d", + output_buffer_len, error_buffer_len); + zbx_snprintf(output_buffer, output_buffer_len, "%d", 9999); + if (strstr(command, "crash") != NULL) + { + zabbix_log(LOG_LEVEL_INFORMATION,"Going to crash with a SEGV"); + /* in the default configuration this results in the zabbix_agentd + log having + 4444: Going to crash with a SEGV + 4444: Got signal [signal:11(SIGSEGV),reason:1,refaddr:(nil)]. Crashing ... + 4444: process producing it's own register and stack dump, + 4444: with the parent then logging the message + 4441: One child process died (PID:4444,exitcode/signal:255). Exiting ... + One could adjust the parent_signal_handler so that the parent did not + exit, and instead just started another child. But of course + there are limits to how much of that sort of thing you want to do. + What it means is that inside your dynamic loaded functions you + really need to catch all your own signals and write + absolutely error free code. + */ + buffer = null_ptr[0]; + } + return(SUCCEED); +} --- /dev/null 2013-08-25 10:31:33.566786381 -0400 +++ zbx-test.sh 2013-08-17 10:38:32.396848123 -0400 @@ -0,0 +1,4 @@ +#!/bin/bash +X=$RANDOM +echo `date '+%Y-%m-%d %H:%M:%S'` return $X from $* >> $HOME/Documents/zbx-test.log +echo $X --- /dev/null 2013-08-25 10:31:33.566786381 -0400 +++ zbx-test1.conf 2013-08-25 11:24:30.167261310 -0400 @@ -0,0 +1,18 @@ + +PidFile=/home/gjc/Documents/zbx.pid +LogFile=/home/gjc/Documents/zbx.log +DebugLevel=4 +Server=127.0.0.1 +ListenPort=19000 +HostnameItem=system.hostname + +UserParameter=mytest,/home/gjc/Documents/zbx-test.sh +UserParameter=mytest_so,/home/gjc/Documents/zbx-test.so,zbx_test_1 + +UserParameter=mytestvar[*],/home/gjc/Documents/zbx-test.sh $1 $2 +UserParameter=mytestvar_so[*],/home/gjc/Documents/zbx-test.so,zbx_test_1 $1 $2 + +UserParameter=mytestvarbadso_so[*],/home/gjc/Documents/zbx-testbad.so,zbx_test_1 $1 $2 +UserParameter=mytestvarbadsym_so[*],/home/gjc/Documents/zbx-test.so,zbx_test_bad $1 $2 + + --- /dev/null 2013-08-25 10:31:33.566786381 -0400 +++ zbx-makefile 2013-08-25 11:27:07.548041694 -0400 @@ -0,0 +1,189 @@ +## name: zbx-makefile +## purpose: convenience target for doing zabbix development. + +CFG=$(HOME)/Documents/zbx-test1.conf + +ZBX_TREE=./zabbix-2.0.7 + +AGENTD=$(ZBX_TREE)/src/zabbix_agent/zabbix_agentd + +ZBX_EXECUTE_SRC=$(ZBX_TREE)/src/libs/zbxexec/execute.c + +DBG= +GDB_PRECMD=gdb --args +TRC_PRECMD=strace -F -o trace.out + +ifeq ($(DBG),gdb) + PRECMD=$(GDB_PRECMD) +endif +ifeq ($(DBG),trace) + PRECMD=$(TRC_PRECMD) +endif + +show: + @echo CFG=$(CFG) + @echo ZBX_TREE=$(ZBX_TREE) + @echo AGENTD=$(AGENTD) + @echo DBG=$(DBG) + @echo PRECMD=$(PRECMD) + +zcfgh: + (cd $(ZBX_TREE);\ + ./configure --help=recursive) + +## a nice server feature here is curl +## the configure program needs to have curl-config program available. +## I'm using postgresql database because the ADMIN gui is +## so much better than what you usually get for mysql. + +zconfig: + (cd $(ZBX_TREE);\ + CFLAGS=-g; \ + export CFLAGS; \ + ./configure \ + --enable-server \ + --enable-agent \ + --with-postgresql \ + --with-libcurl) + +zbuild: + (cd $(ZBX_TREE); make) + +zinstall: + (cd $(ZBX_TREE); make install) + +zclean: + (cd $(ZBX_TREE); make clean) + +show-ldd: + -ldd $(AGENTD) + -ldd zbx-test.so + +zt1: + $(PRECMD) $(AGENTD) --config $(CFG) --print + +zt2: + $(PRECMD) $(AGENTD) --config $(CFG) --test system.localtime[utc] + +zt3: + $(PRECMD) $(AGENTD) --config $(CFG) --test mytest + +zt4: + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvar + +zt5: + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvar[foo] + +zt6: + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvar[foo,bar] + +zt7: zbx-test.so + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvar_so[foo,bar] + +zt8: zbx-test.so + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvar_so[crash] + +zt9: zbx-test.so + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvarbadso_so[x] + +zt10: zbx-test.so + $(PRECMD) $(AGENTD) --config $(CFG) --test mytestvarbadsym_so[x] + + +zbx-setup-orig: + if [ ! -f $(ZBX_EXECUTE_SRC).orig ]; then \ + cp -v $(ZBX_EXECUTE_SRC) $(ZBX_EXECUTE_SRC).orig ; \ + fi + +zbx-diff: + mkdir -p zbx-patches + (pfile="zbx-patches/zbx-patch.`date +%Y-%m-%d-%H-%M-%S`.txt"; \ + diff -u $(ZBX_EXECUTE_SRC).orig $(ZBX_EXECUTE_SRC) > $$pfile; \ + diff -u /dev/null zbx-dyn.h >> $$pfile; \ + diff -u /dev/null zbx-dyn.c >> $$pfile; \ + diff -u /dev/null zbx-test.c >> $$pfile; \ + diff -u /dev/null zbx-test.sh >> $$pfile; \ + diff -u /dev/null zbx-test1.conf >> $$pfile; \ + diff -u /dev/null zbx-makefile >> $$pfile; \ + du -h $$pfile) + +zbx-test.o: zbx-test.c zbx-dyn.h + gcc -g -fPIC -c zbx-test.c + +zbx-dyn.o: zbx-dyn.c zbx-dyn.h + gcc -g -fPIC -c zbx-dyn.c + +zbx-test.so: zbx-test.o zbx-dyn.o + ld -shared -o zbx-test.so zbx-test.o zbx-dyn.o + + +kill-server: + kill -TERM `cat zbx.pid` + +start-server: + $(PRECMD) $(AGENTD) --config $(CFG) + +ps-server: + ps -f --ppid `cat zbx.pid` --pid `cat zbx.pid` + +## Test Plan: +## We want to make sure that dlopen and dlsym are efficient +## enough so that we can just use them straight, without having +## to wrap our own symbol table management around them. +## We also want to compare the overhead of the fork()exec() +## mechanism against the dynamic link way of doing things. +## Attaching gdb to an existing agentd and looking at +## the threading model is also important in deciding +## if we need to use a mutex or not. + +## ps the server so that we can use strace +## on a particular pid of agentd child, +## and then observe the system calls when running zt7b +## on a freshly started agentd. +## One thing I noticed is that the access to the zbx.log +## is by O_RDWR|O_CREAT|O_APPEND +## followed by a write and close. +## This does not absolutely ensure that multiple processes won't +## interlace log file lines if the lines are long enough. +## To be absolutely sure requires the use of fcntl F_SETLKW F_UNLCK. + + +zt2a: + zabbix_get -s 127.0.0.1 -p 19000 -k system.localtime[utc] + +zt5a: + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvar[foo] + +zt7a: + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvar_so[foo,bar] + +zt8a: + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvar_so[crash] + +zt9a: + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvarbadso_so[x] + +zt10a: + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvarbadsym_so[x] + +TEN=0 1 2 3 4 5 6 7 8 9 + +zt2b: + for x in $(TEN); do for y in $(TEN); do \ + zabbix_get -s 127.0.0.1 -p 19000 -k system.localtime[utc] ; \ + done ; done + +zt5b: + for x in $(TEN); do for y in $(TEN); do \ + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvar[foo] ; \ + done ; done + +zt7b: + for x in $(TEN); do for y in $(TEN); do \ + zabbix_get -s 127.0.0.1 -p 19000 -k mytestvar_so[foo,bar] ; \ + done ; done + +zt8b: + + +