From 78c0df8f3eef7ba0a79c10a5c9607192bc9e2f62 Mon Sep 17 00:00:00 2001 From: Jin JinRu Date: Mon, 30 Jul 2018 23:17:44 +0800 Subject: [PATCH] Added retracev2 command line utility --- ci/install.sh | 9 + ci/main.sh | 3 +- configure.ac | 8 +- src/Makefile.am | 4 +- src/v2/Makefile.am | 11 +- src/v2/retrace_v2.c | 407 +++++++++++++++++++++++++++++++++++++++++- src/v2/retrace_v2.h | 36 ++++ src/v2/retrace_v2.nos | 19 +- test/Makefile.am | 4 +- test/cmockatest.c | 2 +- 10 files changed, 479 insertions(+), 24 deletions(-) create mode 100644 src/v2/retrace_v2.h diff --git a/ci/install.sh b/ci/install.sh index 9ada2a35..50cdc3a6 100755 --- a/ci/install.sh +++ b/ci/install.sh @@ -33,3 +33,12 @@ if [ ! -e "${CHECKPATCH_INSTALL}/checkpatch.pl" ]; then patch -p0 < $SPWD/checkpatch.pl.patch echo "invalid.struct.name" > const_structs.checkpatch fi + +# install libnereon +git clone -b v0.9.4 https://github.com/riboseinc/libnereon +cd libnereon +mkdir build +cd build +cmake .. +make +sudo make install diff --git a/ci/main.sh b/ci/main.sh index 895f6210..ac76f9da 100755 --- a/ci/main.sh +++ b/ci/main.sh @@ -30,9 +30,10 @@ CHECKPATCH_FLAGS+=" --ignore SSCANF_TO_KSTRTO" CHECKPATCH_FLAGS+=" --ignore EXECUTE_PERMISSIONS" CHECKPATCH_FLAGS+=" --ignore MULTISTATEMENT_MACRO_USE_DO_WHILE" CHECKPATCH_FLAGS+=" --ignore STORAGE_CLASS" +CHECKPATCH_FLAGS+=" --ignore SPDX_LICENSE_TAG" # checkpatch.pl will ignore the following paths -CHECKPATCH_IGNORE+=" checkpatch.pl.patch Makefile test/Makefile test/http.redirect/hello.txt src/retrace_v2/parson.c src/retrace_v2/parson.h *.json" +CHECKPATCH_IGNORE+=" checkpatch.pl.patch Makefile test/Makefile test/http.redirect/hello.txt src/v2/parson.c src/v2/parson.h *.json" CHECKPATCH_EXCLUDE=$(for p in $CHECKPATCH_IGNORE; do echo ":(exclude)$p" ; done) function _checkpatch() { diff --git a/configure.ac b/configure.ac index 88822e42..dd0f9f6e 100644 --- a/configure.ac +++ b/configure.ac @@ -215,19 +215,21 @@ if test "x$enable_profiling" = "xyes" ; then CFLAGS="$CFLAGS -pg" fi +AC_MSG_CHECKING(whether to enable retrace v2) AC_ARG_ENABLE( [v2], [AS_HELP_STRING([--enable-v2], [enable retrace version 2 @<:@default=no@:>@])], , - [enable_v2="yes"] + [enable_v2="no"] ) +AC_MSG_RESULT($enable_v2) +AM_CONDITIONAL([RTR_V2], [test "x$enable_v2" = "xyes"]) if test "x$enable_v2" = "xyes" ; then AX_CHECK_NEREON([], AC_MSG_ERROR([])) AC_DEFINE(RTR_V2, [1], [Define to use retrace v2]) else - AC_DEFINE(RTR_V2, [1], [Define to use retrace v2]) + AC_DEFINE(RTR_V2, [0], [Define to use retrace v2]) fi -AM_CONDITIONAL([RTR_V2], [test "x$enable_v2" = "xyes"]) AC_MSG_CHECKING(whether to enable rpc) AC_ARG_ENABLE(rpc, diff --git a/src/Makefile.am b/src/Makefile.am index 5ed5ad4f..283dfcdf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 if RTR_V2 -SUBDIRS= v2 +SUBDIRS=v2 else -SUBDIRS= v1 +SUBDIRS=v1 endif diff --git a/src/v2/Makefile.am b/src/v2/Makefile.am index 8221fc1a..aa61b425 100644 --- a/src/v2/Makefile.am +++ b/src/v2/Makefile.am @@ -26,7 +26,7 @@ libretrace_v2_la_SOURCES = \ logger.c \ funcs_symbols.S -libretrace_v2_la_CFLAGS = $(NEREON_INCLUDES) -Wall -Wextra +libretrace_v2_la_CFLAGS = $(NEREON_INCLUDES) -Wall -Wextra -shared -fPIC libretrace_v2_la_LIBADD = $(NEREON_LIBDIR)/libnereon.a if DARWIN @@ -61,11 +61,14 @@ endif bin_PROGRAMS=retrace2 retrace2_SOURCES = \ - retrace_v2.c \ - retrace_v2.nos.c + retrace_v2.nos.c \ + retrace_v2.c retrace_v2.nos.c: retrace_v2.nos - $(NEREON_TO_CC) retrace_v2.nos ./ retrace2 + $(NEREON_TO_CC) retrace_v2.nos ./ rtr2 + +clean-local: + rm -rf retrace_v2.nos.c retrace_v2.nos.h retrace2_CFLAGS = $(NEREON_INCLUDES) -Wall -Wextra retrace2_LDADD = $(NEREON_LIBDIR)/libnereon.a diff --git a/src/v2/retrace_v2.c b/src/v2/retrace_v2.c index 29fbc643..ff0420b4 100644 --- a/src/v2/retrace_v2.c +++ b/src/v2/retrace_v2.c @@ -1,12 +1,415 @@ + +/* + * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "retrace_v2.h" +#include "retrace_v2.nos.h" + +static int g_prog_exit; /* - * main function + * print version + */ + +static void +print_version(void) +{ + printf("%s - %s, Copyright 2017 Ribose Inc \n", + PACKAGE_NAME, + PACKAGE_VERSION); + exit(0); +} + +/* + * signal handler + */ + +static void +sig_handler(const int signum) +{ + fprintf(stderr, "Received signal '%d'\n", signum); + g_prog_exit = 1; +} + +/* + * init signals + */ + +static void +init_signal(void) +{ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGHUP, sig_handler); +} + +/* + * free bin arguments + */ + +static void +free_bin_args(char **bin_args, int bin_args_count) +{ + int i; + + if (bin_args_count == 0) + return; + + for (i = 0; i < bin_args_count; i++) + free(bin_args[i]); + free(bin_args); +} + +/* + * free options */ -int main(int argc, char *argv[]) +static void +free_options(struct rtr2_options *opt) { + if (opt->bin_path) + free(opt->bin_path); + + if (opt->bin_args) + free_bin_args(opt->bin_args, opt->bin_args_count); +} + +/* + * try to find libretrace.so in system path + */ + +static const char *g_syslib_dirs[] = { + "/usr/local/lib", + "/usr/lib", + NULL +}; + +static char * +find_lib_in_sys() +{ + char path[MAX_PATH]; + int find_lib = 0; + + int i; + + /* try to find the path of retrace library */ + memset(path, 0, sizeof(path)); + for (i = 0; g_syslib_dirs[i] != NULL; i++) { + struct stat st; + + snprintf(path, sizeof(path), "%s/%s", g_syslib_dirs[i], RTR2_LIB_NAME); + if (stat(path, &st) == 0) { + find_lib = 1; + break; + } + } + if (!find_lib) + return NULL; + + return strdup(path); +} + +/* + * check whether library is able to be loaded dynamically + */ + +static int +check_dynamic_loadable(const char *lib_path) +{ + void *handle; + + printf("Try to load '%s'... ", lib_path); + + /* try to load library for testing purpose */ + handle = dlopen(lib_path, RTLD_NOW); + if (!handle) { + printf("Failed\n"); + return -1; + } + + printf("Success\n"); + dlclose(handle); + + return 0; +} + +/* + * check executable for binary + */ + +static int +check_executable(const char *bin_path, struct stat *st) +{ + /* check SUID set */ + if ((st->st_mode & S_ISUID) && getuid() != 0) { + fprintf(stderr, "The binary '%s' has SUID bit set\n", bin_path); + return -1; + } + return 0; } + +/* + * get path of binary executable + */ + +static int +check_binary_avail(struct rtr2_options *opt) +{ + char *tok, *brkb = NULL; + struct stat st; + + for (tok = strtok_r(opt->proc_cli, " ", &brkb); tok; tok = strtok_r(NULL, " ", &brkb)) { + if (opt->bin_args_count == 0) + opt->bin_path = strdup(tok); + + opt->bin_args = (char **)realloc(opt->bin_args, (opt->bin_args_count + 1) * sizeof(char **)); + if (!opt->bin_args) + break; + + opt->bin_args[opt->bin_args_count++] = strdup(tok); + } + + /* get path of binary */ + if (!strchr(opt->bin_path, '/')) { + char *sys_path, *bin_full_path = NULL; + + /* try to find in system path */ + sys_path = getenv("PATH"); + if (!sys_path) + return -1; + + for (tok = strtok_r(sys_path, ":", &brkb); tok; tok = strtok_r(NULL, " ", &brkb)) { + bin_full_path = (char *) malloc(strlen(tok) + strlen(opt->bin_path) + 2); + if (!bin_full_path) + return -1; + + memset(bin_full_path, 0, strlen(tok) + strlen(opt->bin_path) + 2); + if (tok[strlen(tok) - 1] == '/') + strcpy(bin_full_path, tok); + else { + strcpy(bin_full_path, tok); + strcat(bin_full_path, "/"); + } + strcat(bin_full_path, opt->bin_path); + + /* check if path is exist */ + if (stat(bin_full_path, &st) == 0) { + free(opt->bin_path); + opt->bin_path = bin_full_path; + break; + } + free(bin_full_path); + bin_full_path = NULL; + } + } + + if (!opt->bin_path) + return -1; + + /* check avaiability of binary */ + if (stat(opt->bin_path, &st) != 0) { + fprintf(stderr, "Coudln't find binary '%s' in system\n", opt->bin_path); + goto end; + } + + if ((st.st_mode & S_IXUSR) && check_executable(opt->bin_path, &st) == 0) + return 0; + +end: + + return -1; +} + +/* + * fork command + */ + +static pid_t +fork_cmd(struct rtr2_options *opt) +{ + pid_t pid; + + pid = fork(); + if (pid == 0) { + /* set environments */ +#ifdef __APPLE__ + setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1); + setenv("DYLD_INSERT_LIBRARIES", opt->lib_path, 1); +#else + setenv("LD_PRELOAD", opt->lib_path, 1); +#endif + if (opt->config_path) + setenv("RETRACE_V2_CONFIG", opt->config_path, 1); + + execv(opt->bin_path, opt->bin_args); + exit(1); + } else if (pid < 0) + return -1; + + return pid; +} + +/* + * parse retrace options + */ + +static int +check_options(struct rtr2_options *opt) +{ + /* find library path */ + if (!opt->lib_path) { + opt->sys_lib_path = find_lib_in_sys(); + opt->lib_path = opt->sys_lib_path; + } + + if (!opt->lib_path || check_dynamic_loadable(opt->lib_path) != 0) { + fprintf(stderr, "Please specify valid path of %s library file\n", RTR2_LIB_NAME); + return -1; + } + + /* check avaiability of command */ + if (check_binary_avail(opt) < 0) { + fprintf(stderr, "Invalid command '%s'\n", opt->proc_cli); + return -1; + } + + return 0; +} + +/* + * trace command + */ + +static void +trace_cmd(struct rtr2_options *opt) +{ + pid_t child_pid; + int w, status; + + child_pid = fork_cmd(opt); + if (child_pid < 0) + return; + + do { + w = waitpid(-1, &status, WNOHANG); + if (w < 0) + break; + + if (g_prog_exit) + kill(-1, SIGTERM); + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); +} + +/* + * main function + */ + +int +main(int argc, char *argv[]) +{ + nereon_ctx_t ctx; + int ret; + bool require_exit = false; + + struct rtr2_options opt; + + struct nereon_config_option cfg_opts[] = { + {"config_file", NEREON_TYPE_CONFIG, false, NULL, &opt.config_path}, + {"proc_cmdline", NEREON_TYPE_STRING, true, NULL, &opt.proc_cli}, + {"lib_path", NEREON_TYPE_STRING, false, NULL, &opt.lib_path}, + {"verbose", NEREON_TYPE_INT, false, NULL, &opt.verbose}, + {"log_path", NEREON_TYPE_STRING, false, NULL, &opt.log_path}, + {"print_version", NEREON_TYPE_BOOL, false, NULL, &opt.print_version}, + }; + + memset(&opt, 0, sizeof(struct rtr2_options)); + + /* initialize nereon context */ + ret = nereon_ctx_init(&ctx, get_rtr2_nos_cfg()); + if (ret != 0) { + fprintf(stderr, "Could not initialize nereon context(err:%s)\n", nereon_get_errmsg()); + exit(1); + } + + /* print command line usage */ + ret = nereon_parse_cmdline(&ctx, argc, argv, &require_exit); + if (ret != 0 || require_exit) { + if (ret != 0) + fprintf(stderr, "Failed to parse command line(err:%s)\n", nereon_get_errmsg()); + + nereon_print_usage(&ctx); + nereon_ctx_finalize(&ctx); + + exit(ret); + } + + ret = nereon_get_config_options(&ctx, cfg_opts, sizeof(cfg_opts) / sizeof(struct nereon_config_option)); + if (ret != 0) { + fprintf(stderr, "Failed to get configuration options(err:%s)\n", nereon_get_errmsg()); + goto end; + } + + if (opt.print_version) { + nereon_ctx_finalize(&ctx); + print_version(); + } + + ret = check_options(&opt); + if (ret != 0) + goto end; + + /* init signal */ + init_signal(); + + /* trace command */ + trace_cmd(&opt); + +end: + /* finalize nereon context */ + nereon_ctx_finalize(&ctx); + + /* free allocated memory for options */ + free_options(&opt); + + return ret; +} diff --git a/src/v2/retrace_v2.h b/src/v2/retrace_v2.h new file mode 100644 index 00000000..ce7f1e2f --- /dev/null +++ b/src/v2/retrace_v2.h @@ -0,0 +1,36 @@ + +#ifndef __RETRACE_V2_H__ +#define __RETRACE_V2_H__ + +#ifndef __APPLE__ +#define RTR2_LIB_NAME "libretrace_v2.so" +#else +#define RTR2_LIB_NAME "libretrace_v2.0.dylib" +#endif + +#define MAX_PATH 256 + +/* + * retrace V2 options + */ + +struct rtr2_options { + char *config_path; + + char *proc_cli; + + char *lib_path; + char *sys_lib_path; + + char *log_path; + int verbose; + + bool print_version; + + /* these options should be free after used */ + char *bin_path; + char **bin_args; + int bin_args_count; +}; + +#endif /* __RETRACE_V2_H__ */ diff --git a/src/v2/retrace_v2.nos b/src/v2/retrace_v2.nos index 5128bce9..1f7e1efa 100644 --- a/src/v2/retrace_v2.nos +++ b/src/v2/retrace_v2.nos @@ -9,26 +9,27 @@ config_option config_file { } cmdline description { - short = "config file" + short = "" long = "set configuration file" } env = RETRACE_V2_CONFIG } -config_option bin_path { +config_option proc_cmdline { type = string + mandatory = true cmdline switch { - short = "-b" + short = "-p" } cmdline description { - short = "binary path" - long = "set binary path to be traced" + short = "" + long = "set process command line to be traced" } - env = RETRACE_V2_TRACE_BIN + env = RETRACE_V2_CMDLINE } config_option lib_path { @@ -39,7 +40,7 @@ config_option lib_path { } cmdline description { - short = "retrace v2 library path" + short = "" long = "set retrace v2 library path" } @@ -54,7 +55,7 @@ config_option verbose { } cmdline description { - short = "log level" + short = "" long = "set log verbosity level(0 : SILENT, 1 : NORMAL, 2 : VERBOSE, 3 : DEBUG)" } @@ -69,7 +70,7 @@ config_option log_path { } cmdline description { - short = "log file" + short = "" long = "specify log file" } diff --git a/test/Makefile.am b/test/Makefile.am index b36db35d..0f2413c8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = $(TESTS_CFLAGS) -I../src/ -g +AM_CFLAGS = $(TESTS_CFLAGS) -I../src/v1 -g dist_check_SCRIPTS = runtests.sh noinst_PROGRAMS = @@ -9,7 +9,7 @@ if CMOCKA if LINUX noinst_PROGRAMS += cmockatest TESTS += cmockatest -cmockatest_CFLAGS = $(CMOCKA_CFLAGS) -I../src/ +cmockatest_CFLAGS = $(CMOCKA_CFLAGS) -I../src/v1 cmockatest_LDADD = $(CMOCKA_LDFLAGS) $(CMOCKA_LIBS) endif endif diff --git a/test/cmockatest.c b/test/cmockatest.c index fef2509a..38ed6a04 100644 --- a/test/cmockatest.c +++ b/test/cmockatest.c @@ -1258,7 +1258,7 @@ main(void) cmocka_unit_test(test_rtr_getaddrinfo), cmocka_unit_test(test_rtr_freeaddrinfo) }; - handle = dlopen("../src/.libs/libretrace.so", RTLD_LAZY); + handle = dlopen("../src/v1/.libs/libretrace.so", RTLD_LAZY); if (!handle) { fputs(dlerror(), stderr); return 1;