From 1670bffdf312795f277237062cca264967f13fd0 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Tue, 7 Feb 2017 01:22:10 -0500 Subject: [PATCH] Add bt_plugin_create_from_name() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This new function creates a new bt_plugin object from a simple plugin name. It returns the first plugin which has the given name within the following directories, in this order: 1. The colon-separated directories listed in the BABELTRACE_PLUGIN_PATH environment variable. 2. ~/.local/lib/babeltrace/plugins 3. $libdir/babeltrace/plugins, where $libdir is the value of --libdir at configure time (/usr/local/lib by default, typically /usr/lib when installed through a distribution). 4. The statically built-in plugins. This makes it easier for any application using libbabeltrace to find a Babeltrace plugin in a standard way without having to know the exact location of the plugin file: ctf_plugin = bt_plugin_create_from_name("ctf"); Since babeltrace(1) also does this search, but has a --plugin-path parameter to insert other directories at a specific place within the search list, the common functions are put in the new common/libbabeltrace-common.la convenience library. This is where new (and existing) common functions between libbabeltrace and other parts of the source tree (converter, plugins, etc.) should be put. Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- Makefile.am | 2 +- common/Makefile.am | 7 ++ common/common.c | 165 +++++++++++++++++++++++++++ configure.ac | 1 + converter/Makefile.am | 3 +- converter/babeltrace-cfg.c | 118 ++++++------------- include/Makefile.am | 1 + include/babeltrace/common-internal.h | 18 +++ include/babeltrace/plugin/plugin.h | 2 + lib/Makefile.am | 3 +- lib/plugin/plugin.c | 134 ++++++++++++++++++++++ 11 files changed, 371 insertions(+), 83 deletions(-) create mode 100644 common/Makefile.am create mode 100644 common/common.c create mode 100644 include/babeltrace/common-internal.h diff --git a/Makefile.am b/Makefile.am index 0544bd16..4b517db7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = include types compat lib formats plugins converter bindings tests doc extras +SUBDIRS = include common types compat lib formats plugins converter bindings tests doc extras dist_doc_DATA = ChangeLog LICENSE mit-license.txt gpl-2.0.txt \ std-ext-lib.txt README diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 00000000..a4543ddc --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,7 @@ +AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include \ + -DINSTALL_LIBDIR=\"$(libdir)\" + + +noinst_LTLIBRARIES = libbabeltrace-common.la + +libbabeltrace_common_la_SOURCES = common.c diff --git a/common/common.c b/common/common.c new file mode 100644 index 00000000..47186e16 --- /dev/null +++ b/common/common.c @@ -0,0 +1,165 @@ +/* + * Babeltrace common functions + * + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins" +#define HOME_ENV_VAR "HOME" +#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins" + +BT_HIDDEN +const char *bt_common_get_system_plugin_path(void) +{ + return SYSTEM_PLUGIN_PATH; +} + +BT_HIDDEN +bool bt_common_is_setuid_setgid(void) +{ + return (geteuid() != getuid() || getegid() != getgid()); +} + +static char *bt_secure_getenv(const char *name) +{ + if (bt_common_is_setuid_setgid()) { + printf_error("Disregarding %s environment variable for setuid/setgid binary", + name); + return NULL; + } + return getenv(name); +} + +static const char *get_home_dir(void) +{ + char *val = NULL; + struct passwd *pwd; + + val = bt_secure_getenv(HOME_ENV_VAR); + if (val) { + goto end; + } + /* Fallback on password file. */ + pwd = getpwuid(getuid()); + if (!pwd) { + goto end; + } + val = pwd->pw_dir; +end: + return val; +} + +BT_HIDDEN +char *bt_common_get_home_plugin_path(void) +{ + char *path = NULL; + const char *home_dir; + + home_dir = get_home_dir(); + if (!home_dir) { + goto end; + } + + if (strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1 >= PATH_MAX) { + printf_error("Home directory path is too long: `%s`\n", + home_dir); + goto end; + } + + path = malloc(PATH_MAX); + if (!path) { + goto end; + } + + strcpy(path, home_dir); + strcat(path, HOME_PLUGIN_SUBPATH); + +end: + return path; +} + +BT_HIDDEN +int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs) +{ + int ret = 0; + const char *at; + const char *end; + size_t init_dirs_len; + + assert(dirs); + init_dirs_len = dirs->len; + + if (!paths) { + /* Nothing to append */ + goto end; + } + + at = paths; + end = paths + strlen(paths); + + while (at < end) { + GString *path; + const char *next_colon; + + next_colon = strchr(at, ':'); + if (next_colon == at) { + /* + * Empty path: try next character (supported + * to conform to the typical parsing of $PATH). + */ + at++; + continue; + } else if (!next_colon) { + /* No more colon: use the remaining */ + next_colon = paths + strlen(paths); + } + + path = g_string_new(NULL); + if (!path) { + goto error; + } + + g_string_append_len(path, at, next_colon - at); + at = next_colon + 1; + g_ptr_array_add(dirs, path); + } + + goto end; + +error: + ret = -1; + + /* Remove the new entries in dirs */ + while (dirs->len > init_dirs_len) { + g_ptr_array_remove_index(dirs, init_dirs_len); + } + +end: + return ret; +} diff --git a/configure.ac b/configure.ac index f4902254..de5553b9 100644 --- a/configure.ac +++ b/configure.ac @@ -438,6 +438,7 @@ AS_IF([test "x$enable_api_doc" = "xyes"], [ AC_CONFIG_FILES([ Makefile types/Makefile + common/Makefile compat/Makefile formats/Makefile formats/ctf/Makefile diff --git a/converter/Makefile.am b/converter/Makefile.am index 6c8db944..35c64ec5 100644 --- a/converter/Makefile.am +++ b/converter/Makefile.am @@ -25,7 +25,8 @@ babeltrace_bin_LDADD = \ $(top_builddir)/formats/ctf-text/libbabeltrace-ctf-text.la \ $(top_builddir)/formats/ctf-metadata/libbabeltrace-ctf-metadata.la \ $(top_builddir)/formats/bt-dummy/libbabeltrace-dummy.la \ - $(top_builddir)/formats/lttng-live/libbabeltrace-lttng-live.la + $(top_builddir)/formats/lttng-live/libbabeltrace-lttng-live.la \ + $(top_builddir)/common/libbabeltrace-common.la if ENABLE_DEBUG_INFO babeltrace_bin_LDADD += $(top_builddir)/lib/libdebug-info.la diff --git a/converter/babeltrace-cfg.c b/converter/babeltrace-cfg.c index 5811f444..608b6548 100644 --- a/converter/babeltrace-cfg.c +++ b/converter/babeltrace-cfg.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,11 +38,8 @@ #include #include "babeltrace-cfg.h" -#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins" #define DEFAULT_SOURCE_COMPONENT_NAME "ctf.fs" #define DEFAULT_SINK_COMPONENT_NAME "text.text" -#define HOME_ENV_VAR "HOME" -#define HOME_SUBPATH "/.babeltrace/plugins" /* * Error printf() macro which prepends "Error: " the first time it's @@ -970,6 +968,11 @@ bool is_setuid_setgid(void) return (geteuid() != getuid() || getegid() != getgid()); } +static void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + /* * Extracts the various paths from the string arg, delimited by ':', * and converts them to an array value object. @@ -982,46 +985,31 @@ static enum bt_value_status plugin_paths_from_arg(struct bt_value *plugin_paths, const char *arg) { - const char *at = arg; - const char *end = arg + strlen(arg); + enum bt_value_status status = BT_VALUE_STATUS_OK; + GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); + int ret; + size_t i; - while (at < end) { - int ret; - GString *path; - const char *next_colon; + if (!dirs) { + status = BT_VALUE_STATUS_ERROR; + goto end; + } - next_colon = strchr(at, ':'); - if (next_colon == at) { - /* - * Empty path: try next character (supported - * to conform to the typical parsing of $PATH). - */ - at++; - continue; - } else if (!next_colon) { - /* No more colon: use the remaining */ - next_colon = arg + strlen(arg); - } + ret = bt_common_append_plugin_path_dirs(arg, dirs); + if (ret) { + status = BT_VALUE_STATUS_ERROR; + goto end; + } - path = g_string_new(NULL); - if (!path) { - print_err_oom(); - goto error; - } + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); - g_string_append_len(path, at, next_colon - at); - at = next_colon + 1; - ret = bt_value_array_append_string(plugin_paths, path->str); - g_string_free(path, TRUE); - if (ret) { - print_err_oom(); - goto error; - } + bt_value_array_append_string(plugin_paths, dir->str); } - return BT_VALUE_STATUS_OK; -error: - return BT_VALUE_STATUS_ERROR; +end: + g_ptr_array_free(dirs, TRUE); + return status; } /* @@ -2254,53 +2242,23 @@ not_found: return -1; } -static char *bt_secure_getenv(const char *name) -{ - if (is_setuid_setgid()) { - printf_err("Disregarding %s environment variable for setuid/setgid binary", name); - return NULL; - } - return getenv(name); -} - -static const char *get_home_dir(void) -{ - char *val = NULL; - struct passwd *pwd; - - val = bt_secure_getenv(HOME_ENV_VAR); - if (val) { - goto end; - } - /* Fallback on password file. */ - pwd = getpwuid(getuid()); - if (!pwd) { - goto end; - } - val = pwd->pw_dir; -end: - return val; -} - static int add_internal_plugin_paths(struct bt_config *cfg) { - if (!cfg->omit_home_plugin_path) { - char path[PATH_MAX]; - const char *home_dir; + int ret; - if (is_setuid_setgid()) { + if (!cfg->omit_home_plugin_path) { + if (bt_common_is_setuid_setgid()) { printf_debug("Skipping non-system plugin paths for setuid/setgid binary."); } else { - home_dir = get_home_dir(); - if (home_dir) { - if (strlen(home_dir) + strlen(HOME_SUBPATH) + 1 - >= PATH_MAX) { - printf_err("Home directory path too long\n"); - goto error; - } - strcpy(path, home_dir); - strcat(path, HOME_SUBPATH); - if (plugin_paths_from_arg(cfg->plugin_paths, path)) { + char *home_plugin_dir = + bt_common_get_home_plugin_path(); + + if (home_plugin_dir) { + ret = plugin_paths_from_arg(cfg->plugin_paths, + home_plugin_dir); + free(home_plugin_dir); + + if (ret) { printf_err("Invalid home plugin path\n"); goto error; } @@ -2310,7 +2268,7 @@ static int add_internal_plugin_paths(struct bt_config *cfg) if (!cfg->omit_system_plugin_path) { if (plugin_paths_from_arg(cfg->plugin_paths, - SYSTEM_PLUGIN_PATH)) { + bt_common_get_system_plugin_path())) { printf_err("Invalid system plugin path\n"); goto error; } diff --git a/include/Makefile.am b/include/Makefile.am index b05e6469..dbd49630 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -68,6 +68,7 @@ noinst_HEADERS = \ babeltrace/babeltrace-internal.h \ babeltrace/bitfield.h \ babeltrace/clock-internal.h \ + babeltrace/common-internal.h \ babeltrace/compiler.h \ babeltrace/context-internal.h \ babeltrace/format-internal.h \ diff --git a/include/babeltrace/common-internal.h b/include/babeltrace/common-internal.h new file mode 100644 index 00000000..64299e9d --- /dev/null +++ b/include/babeltrace/common-internal.h @@ -0,0 +1,18 @@ +#ifndef BABELTRACE_COMMON_INTERNAL_H +#define BABELTRACE_COMMON_INTERNAL_H + +#include + +BT_HIDDEN +bool bt_common_is_setuid_setgid(void); + +BT_HIDDEN +const char *bt_common_get_system_plugin_path(void); + +BT_HIDDEN +char *bt_common_get_home_plugin_path(void); + +BT_HIDDEN +int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs); + +#endif /* BABELTRACE_COMMON_INTERNAL_H */ diff --git a/include/babeltrace/plugin/plugin.h b/include/babeltrace/plugin/plugin.h index 9bb04f2e..0ec3b6c0 100644 --- a/include/babeltrace/plugin/plugin.h +++ b/include/babeltrace/plugin/plugin.h @@ -51,6 +51,8 @@ enum bt_plugin_status { BT_PLUGIN_STATUS_NOMEM = -4, }; +extern struct bt_plugin *bt_plugin_create_from_name(const char *plugin_name); + extern struct bt_plugin **bt_plugin_create_all_from_file(const char *path); extern struct bt_plugin **bt_plugin_create_all_from_dir(const char *path, diff --git a/lib/Makefile.am b/lib/Makefile.am index 71af87a5..c47d581b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -32,4 +32,5 @@ libbabeltrace_la_LIBADD = \ $(top_builddir)/types/libbabeltrace_types.la \ $(top_builddir)/compat/libcompat.la \ component/libcomponent.la \ - plugin/libplugin.la + plugin/libplugin.la \ + $(top_builddir)/common/libbabeltrace-common.la diff --git a/lib/plugin/plugin.c b/lib/plugin/plugin.c index ce3a866f..0e628890 100644 --- a/lib/plugin/plugin.c +++ b/lib/plugin/plugin.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -180,6 +181,139 @@ end: return plugins; } +static void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + +void free_plugins(struct bt_plugin **plugins) { + if (plugins) { + struct bt_plugin **cur_plugin = plugins; + + while (*cur_plugin) { + bt_put(*cur_plugin); + cur_plugin++; + } + + free(plugins); + } +} + +struct bt_plugin *bt_plugin_create_from_name(const char *plugin_name) +{ + const char *system_plugin_dir; + char *home_plugin_dir = NULL; + const char *envvar; + struct bt_plugin *plugin = NULL; + struct bt_plugin **plugins = NULL; + struct bt_plugin **cur_plugin; + GPtrArray *dirs = NULL; + int ret; + size_t i; + + if (!plugin_name) { + goto end; + } + + dirs = g_ptr_array_new_with_free_func((GDestroyNotify) destroy_gstring); + if (!dirs) { + goto end; + } + + /* + * Search order is: + * + * 1. BABELTRACE_PLUGIN_PATH environment variable + * (colon-separated list of directories) + * 2. ~/.local/lib/babeltrace/plugins + * 3. Default system directory for Babeltrace plugins, usually + * /usr/lib/babeltrace/plugins or + * /usr/local/lib/babeltrace/plugins if installed + * locally + * 4. Built-in plugins (static) + * + * Directories are searched non-recursively. + */ + envvar = getenv("BABELTRACE_PLUGIN_PATH"); + if (envvar) { + ret = bt_common_append_plugin_path_dirs(envvar, dirs); + if (ret) { + goto end; + } + } + + home_plugin_dir = bt_common_get_home_plugin_path(); + if (home_plugin_dir) { + GString *home_plugin_dir_str = + g_string_new(home_plugin_dir); + + if (!home_plugin_dir_str) { + goto end; + } + + g_ptr_array_add(dirs, home_plugin_dir_str); + } + + system_plugin_dir = bt_common_get_system_plugin_path(); + if (system_plugin_dir) { + GString *system_plugin_dir_str = + g_string_new(system_plugin_dir); + + if (!system_plugin_dir_str) { + goto end; + } + + g_ptr_array_add(dirs, system_plugin_dir_str); + } + + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); + + printf_verbose("Trying to load plugins from directory `%s`\n", + dir->str); + free_plugins(plugins); + plugins = bt_plugin_create_all_from_dir(dir->str, false); + if (!plugins) { + continue; + } + + cur_plugin = plugins; + + while (*cur_plugin) { + if (strcmp(bt_plugin_get_name(*cur_plugin), plugin_name) + == 0) { + plugin = bt_get(*cur_plugin); + goto end; + } + + cur_plugin++; + } + } + + free_plugins(plugins); + plugins = bt_plugin_create_all_from_static(); + cur_plugin = plugins; + + while (*cur_plugin) { + if (strcmp(bt_plugin_get_name(*cur_plugin), plugin_name) == 0) { + plugin = bt_get(*cur_plugin); + goto end; + } + + cur_plugin++; + } + +end: + free(home_plugin_dir); + free_plugins(plugins); + + if (dirs) { + g_ptr_array_free(dirs, TRUE); + } + + return plugin; +} + /* Allocate dirent as recommended by READDIR(3), NOTES on readdir_r */ static struct dirent *alloc_dirent(const char *path) -- 2.34.1