From: Simon Marchi Date: Fri, 3 Sep 2021 21:31:29 +0000 (-0400) Subject: common: compile libcompat as C++ X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=740da7d5000ca1ffdcf14bda5096bf7ccfb86bdd;p=lttng-tools.git common: compile libcompat as C++ I got errors like these for programs that use libcompat (usually through libcommon), but are still linked with gcc, rather than g++: CCLD filter-grammar-test /usr/bin/ld: ./.libs/libcommon.a(directory-handle.o):(.data.rel.local.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `_ _gxx_personality_v0' Automake still links them with gcc, because they don't contain any C++ source directly. Fix that by changing them to be C++ source. Change-Id: I3eeca3d9af8940795b69f48d306f282ae0b08589 Signed-off-by: Simon Marchi Signed-off-by: Jérémie Galarneau --- diff --git a/include/lttng/action/rate-policy-internal.h b/include/lttng/action/rate-policy-internal.h index c04cabcb2..2f8267a31 100644 --- a/include/lttng/action/rate-policy-internal.h +++ b/include/lttng/action/rate-policy-internal.h @@ -11,8 +11,13 @@ #include #include #include +#include #include +#ifdef __cplusplus +extern "C" { +#endif + struct mi_writer; int lttng_rate_policy_serialize(struct lttng_rate_policy *rate_policy, @@ -37,4 +42,8 @@ enum lttng_error_code lttng_rate_policy_mi_serialize( const struct lttng_rate_policy *policy, struct mi_writer *writer); +#ifdef __cplusplus +} +#endif + #endif /* LTTNG_RATE_POLICY */ diff --git a/include/lttng/event-expr-internal.h b/include/lttng/event-expr-internal.h index 469fd17bb..d40f2fa01 100644 --- a/include/lttng/event-expr-internal.h +++ b/include/lttng/event-expr-internal.h @@ -11,6 +11,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + struct lttng_bytecode; struct mi_writer; @@ -64,4 +68,8 @@ enum lttng_error_code lttng_event_expr_mi_serialize( const struct lttng_event_expr *expression, struct mi_writer *writer); +#ifdef __cplusplus +} +#endif + #endif /* LTTNG_EVENT_EXPR_INTERNAL_H */ diff --git a/include/lttng/kernel-probe-internal.h b/include/lttng/kernel-probe-internal.h index 06c3d7c7e..e0b94749e 100644 --- a/include/lttng/kernel-probe-internal.h +++ b/include/lttng/kernel-probe-internal.h @@ -11,10 +11,15 @@ #include #include #include +#include #include #include #include +#ifdef __cplusplus +extern "C" { +#endif + struct lttng_payload; struct lttng_payload_view; struct lttng_dynamic_buffer; @@ -106,4 +111,8 @@ enum lttng_error_code lttng_kernel_probe_location_mi_serialize( const struct lttng_kernel_probe_location *location, struct mi_writer *writer); +#ifdef __cplusplus +} +#endif + #endif /* LTTNG_KERNEL_PROBE_INTERNAL_H */ diff --git a/include/lttng/log-level-rule-internal.h b/include/lttng/log-level-rule-internal.h index b02afafe0..26abaa862 100644 --- a/include/lttng/log-level-rule-internal.h +++ b/include/lttng/log-level-rule-internal.h @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 5d1904d9c..cb8ac7b10 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -175,7 +175,7 @@ noinst_HEADERS = \ utils.h noinst_PROGRAMS = filter-grammar-test -filter_grammar_test_SOURCES = filter-grammar-test.c +filter_grammar_test_SOURCES = filter-grammar-test.cpp filter_grammar_test_LDADD = \ libcommon.la diff --git a/src/common/compat/Makefile.am b/src/common/compat/Makefile.am index a8f2c1001..0499b3568 100644 --- a/src/common/compat/Makefile.am +++ b/src/common/compat/Makefile.am @@ -2,8 +2,23 @@ noinst_LTLIBRARIES = libcompat.la -libcompat_la_SOURCES = poll.c poll.h fcntl.h endian.h mman.h dirent.h \ - socket.h compat-fcntl.c tid.h \ - getenv.h string.h paths.h pthread.h netdb.h \ - time.h directory-handle.h directory-handle.c path.h \ - errno.h +libcompat_la_SOURCES = \ + compat-fcntl.cpp \ + directory-handle.cpp \ + directory-handle.h \ + dirent.h \ + endian.h \ + errno.h \ + fcntl.h \ + getenv.h \ + mman.h \ + netdb.h \ + path.h \ + paths.h \ + poll.cpp \ + poll.h \ + pthread.h \ + socket.h \ + string.h \ + tid.h \ + time.h diff --git a/src/common/compat/compat-fcntl.c b/src/common/compat/compat-fcntl.c deleted file mode 100644 index bc014d640..000000000 --- a/src/common/compat/compat-fcntl.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include - -#ifdef __linux__ - -int compat_sync_file_range(int fd, off64_t offset, off64_t nbytes, - unsigned int flags) -{ -#ifdef HAVE_SYNC_FILE_RANGE - return sync_file_range(fd, offset, nbytes, flags); -#else - return fdatasync(fd); -#endif -} - -#endif /* __linux__ */ diff --git a/src/common/compat/compat-fcntl.cpp b/src/common/compat/compat-fcntl.cpp new file mode 100644 index 000000000..bc014d640 --- /dev/null +++ b/src/common/compat/compat-fcntl.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include + +#ifdef __linux__ + +int compat_sync_file_range(int fd, off64_t offset, off64_t nbytes, + unsigned int flags) +{ +#ifdef HAVE_SYNC_FILE_RANGE + return sync_file_range(fd, offset, nbytes, flags); +#else + return fdatasync(fd); +#endif +} + +#endif /* __linux__ */ diff --git a/src/common/compat/directory-handle.c b/src/common/compat/directory-handle.c deleted file mode 100644 index 9b98e0482..000000000 --- a/src/common/compat/directory-handle.c +++ /dev/null @@ -1,1386 +0,0 @@ -/* - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* - * This compatibility layer shares a common "base" that is implemented - * in terms of an internal API. This file contains two implementations - * of the internal API below. - */ -static -int lttng_directory_handle_mkdir( - const struct lttng_directory_handle *handle, - const char *path, mode_t mode); -static -int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path, - mode_t mode, uid_t uid, gid_t gid); -static -int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, - const char *path, mode_t mode, uid_t uid, gid_t gid); -static -int lttng_directory_handle_open(const struct lttng_directory_handle *handle, - const char *filename, int flags, mode_t mode); -static -int _run_as_open(const struct lttng_directory_handle *handle, - const char *filename, - int flags, mode_t mode, uid_t uid, gid_t gid); -static -int lttng_directory_handle_unlink( - const struct lttng_directory_handle *handle, - const char *filename); -static -int _run_as_unlink(const struct lttng_directory_handle *handle, - const char *filename, uid_t uid, gid_t gid); -static -int _lttng_directory_handle_rename( - const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name); -static -int _run_as_rename(const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name, uid_t uid, gid_t gid); -static -DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, - const char *path); -static -int lttng_directory_handle_rmdir( - const struct lttng_directory_handle *handle, const char *name); -static -int _run_as_rmdir(const struct lttng_directory_handle *handle, - const char *name, uid_t uid, gid_t gid); -static -int _run_as_rmdir_recursive( - const struct lttng_directory_handle *handle, const char *name, - uid_t uid, gid_t gid, int flags); -static -void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle); -static -void lttng_directory_handle_release(struct urcu_ref *ref); - -#ifdef HAVE_DIRFD - -/* - * Special inode number reserved to represent the "current working directory". - * ino_t is spec'ed as being an unsigned integral type. - */ -#define RESERVED_AT_FDCWD_INO \ - ({ \ - uint64_t reserved_val; \ - switch (sizeof(ino_t)) { \ - case 4: \ - reserved_val = UINT32_MAX; \ - break; \ - case 8: \ - reserved_val = UINT64_MAX; \ - break; \ - default: \ - abort(); \ - } \ - (ino_t) reserved_val; \ - }) - -struct lttng_directory_handle *lttng_directory_handle_create(const char *path) -{ - const struct lttng_directory_handle cwd_handle = { - .dirfd = AT_FDCWD, - }; - - /* Open a handle to the CWD if NULL is passed. */ - return lttng_directory_handle_create_from_handle(path, &cwd_handle); -} - -struct lttng_directory_handle *lttng_directory_handle_create_from_handle( - const char *path, - const struct lttng_directory_handle *ref_handle) -{ - int dirfd; - struct lttng_directory_handle *handle = NULL; - - if (!path) { - handle = lttng_directory_handle_copy(ref_handle); - goto end; - } - if (!*path) { - ERR("Failed to initialize directory handle: provided path is an empty string"); - goto end; - } - - dirfd = openat(ref_handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); - if (dirfd == -1) { - PERROR("Failed to initialize directory handle to \"%s\"", path); - goto end; - } - - handle = lttng_directory_handle_create_from_dirfd(dirfd); - if (!handle) { - goto error_close; - } -end: - return handle; -error_close: - if (close(dirfd)) { - PERROR("Failed to close directory file descriptor"); - } - return NULL; -} - -struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd( - int dirfd) -{ - int ret; - struct lttng_directory_handle *handle = zmalloc(sizeof(*handle)); - struct stat stat_buf; - - if (!handle) { - goto end; - } - - if (dirfd != AT_FDCWD) { - ret = fstat(dirfd, &stat_buf); - if (ret) { - PERROR("Failed to fstat directory file descriptor %i", dirfd); - lttng_directory_handle_release(&handle->ref); - handle = NULL; - goto end; - } - } else { - handle->directory_inode = RESERVED_AT_FDCWD_INO; - } - handle->dirfd = dirfd; - urcu_ref_init(&handle->ref); -end: - return handle; -} - -static -void lttng_directory_handle_release(struct urcu_ref *ref) -{ - int ret; - struct lttng_directory_handle *handle = - container_of(ref, struct lttng_directory_handle, ref); - - if (handle->destroy_cb) { - handle->destroy_cb(handle, handle->destroy_cb_data); - } - - if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) { - goto end; - } - ret = close(handle->dirfd); - if (ret == -1) { - PERROR("Failed to close directory file descriptor of directory handle"); - } -end: - lttng_directory_handle_invalidate(handle); - free(handle); -} - -struct lttng_directory_handle *lttng_directory_handle_copy( - const struct lttng_directory_handle *handle) -{ - struct lttng_directory_handle *new_handle = NULL; - - if (handle->dirfd == AT_FDCWD) { - new_handle = lttng_directory_handle_create_from_dirfd(AT_FDCWD); - } else { - const int new_dirfd = dup(handle->dirfd); - - if (new_dirfd == -1) { - PERROR("Failed to duplicate directory file descriptor of directory handle"); - goto end; - } - new_handle = lttng_directory_handle_create_from_dirfd( - new_dirfd); - if (!new_handle && close(new_dirfd)) { - PERROR("Failed to close directory file descriptor of directory handle"); - } - } -end: - return new_handle; -} - -bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs, - const struct lttng_directory_handle *rhs) -{ - return lhs->directory_inode == rhs->directory_inode; -} - -static -void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) -{ - handle->dirfd = -1; -} - -int lttng_directory_handle_stat(const struct lttng_directory_handle *handle, - const char *path, struct stat *st) -{ - return fstatat(handle->dirfd, path, st, 0); -} - -bool lttng_directory_handle_uses_fd( - const struct lttng_directory_handle *handle) -{ - return handle->dirfd != AT_FDCWD; -} - -static -int lttng_directory_handle_mkdir( - const struct lttng_directory_handle *handle, - const char *path, mode_t mode) -{ - return mkdirat(handle->dirfd, path, mode); -} - -static -int lttng_directory_handle_open(const struct lttng_directory_handle *handle, - const char *filename, int flags, mode_t mode) -{ - return openat(handle->dirfd, filename, flags, mode); -} - -static -int _run_as_open(const struct lttng_directory_handle *handle, - const char *filename, - int flags, mode_t mode, uid_t uid, gid_t gid) -{ - return run_as_openat(handle->dirfd, filename, flags, mode, uid, gid); -} - -static -int _run_as_unlink(const struct lttng_directory_handle *handle, - const char *filename, uid_t uid, gid_t gid) -{ - return run_as_unlinkat(handle->dirfd, filename, uid, gid); -} - -static -int lttng_directory_handle_unlink( - const struct lttng_directory_handle *handle, - const char *filename) -{ - return unlinkat(handle->dirfd, filename, 0); -} - -static -int _run_as_mkdir(const struct lttng_directory_handle *handle, - const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - return run_as_mkdirat(handle->dirfd, path, mode, uid, gid); -} - -static -int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, - const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid); -} - -static -int _lttng_directory_handle_rename( - const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name) -{ - return renameat(old_handle->dirfd, old_name, - new_handle->dirfd, new_name); -} - -static -int _run_as_rename(const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name, uid_t uid, gid_t gid) -{ - return run_as_renameat(old_handle->dirfd, old_name, new_handle->dirfd, - new_name, uid, gid); -} - -static -DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, - const char *path) -{ - DIR *dir_stream = NULL; - int fd = openat(handle->dirfd, path, O_RDONLY); - - if (fd < 0) { - goto end; - } - - dir_stream = fdopendir(fd); - if (!dir_stream) { - int ret; - - PERROR("Failed to open directory stream"); - ret = close(fd); - if (ret) { - PERROR("Failed to close file descriptor to %s", path); - } - goto end; - } - -end: - return dir_stream; -} - -static -int lttng_directory_handle_rmdir( - const struct lttng_directory_handle *handle, const char *name) -{ - int ret = unlinkat(handle->dirfd, name, AT_REMOVEDIR); - if (ret) { - PERROR("Failed to remove directory `%s`", name); - } - - return ret; -} - -static -int _run_as_rmdir(const struct lttng_directory_handle *handle, - const char *name, uid_t uid, gid_t gid) -{ - return run_as_rmdirat(handle->dirfd, name, uid, gid); -} - -static -int _run_as_rmdir_recursive( - const struct lttng_directory_handle *handle, const char *name, - uid_t uid, gid_t gid, int flags) -{ - return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid, flags); -} - -#else /* HAVE_DIRFD */ - -static -int get_full_path(const struct lttng_directory_handle *handle, - const char *subdirectory, char *fullpath, size_t size) -{ - int ret; - const bool subdirectory_is_absolute = - subdirectory && *subdirectory == '/'; - const char * const base = subdirectory_is_absolute ? - subdirectory : handle->base_path; - const char * const end = subdirectory && !subdirectory_is_absolute ? - subdirectory : NULL; - const size_t base_len = strlen(base); - const size_t end_len = end ? strlen(end) : 0; - const bool add_separator_slash = end && base[base_len - 1] != '/'; - const bool add_trailing_slash = end && end[end_len - 1] != '/'; - - ret = snprintf(fullpath, size, "%s%s%s%s", - base, - add_separator_slash ? "/" : "", - end ? end : "", - add_trailing_slash ? "/" : ""); - if (ret == -1 || ret >= size) { - ERR("Failed to format subdirectory from directory handle"); - ret = -1; - goto end; - } - ret = 0; -end: - return ret; -} - -static -struct lttng_directory_handle *_lttng_directory_handle_create(char *path) -{ - struct lttng_directory_handle *handle = zmalloc(sizeof(*handle)); - - if (!handle) { - goto end; - } - urcu_ref_init(&handle->ref); - handle->base_path = path; -end: - return handle; -} - -struct lttng_directory_handle *lttng_directory_handle_create( - const char *path) -{ - int ret; - const char *cwd = ""; - size_t cwd_len, path_len; - char cwd_buf[LTTNG_PATH_MAX] = {}; - char handle_buf[LTTNG_PATH_MAX] = {}; - struct lttng_directory_handle *new_handle = NULL; - bool add_cwd_slash = false, add_trailing_slash = false; - const struct lttng_directory_handle cwd_handle = { - .base_path = handle_buf, - }; - - path_len = path ? strlen(path) : 0; - add_trailing_slash = path && path[path_len - 1] != '/'; - if (!path || (path && *path != '/')) { - cwd = getcwd(cwd_buf, sizeof(cwd_buf)); - if (!cwd) { - PERROR("Failed to initialize directory handle, can't get current working directory"); - ret = -1; - goto end; - } - cwd_len = strlen(cwd); - if (cwd_len == 0) { - ERR("Failed to initialize directory handle, current working directory path has a length of 0"); - ret = -1; - goto end; - } - add_cwd_slash = cwd[cwd_len - 1] != '/'; - } - - ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s", - cwd, - add_cwd_slash ? "/" : "", - path ? : "", - add_trailing_slash ? "/" : ""); - if (ret == -1 || ret >= LTTNG_PATH_MAX) { - ERR("Failed to initialize directory handle, failed to format directory path"); - goto end; - } - - new_handle = lttng_directory_handle_create_from_handle(path, &cwd_handle); -end: - return new_handle; -} - -struct lttng_directory_handle *lttng_directory_handle_create_from_handle( - const char *path, - const struct lttng_directory_handle *ref_handle) -{ - int ret; - size_t path_len, handle_path_len; - bool add_trailing_slash; - struct stat stat_buf; - struct lttng_directory_handle *new_handle = NULL; - char *new_path = NULL; - - LTTNG_ASSERT(ref_handle && ref_handle->base_path); - - ret = lttng_directory_handle_stat(ref_handle, path, &stat_buf); - if (ret == -1) { - PERROR("Failed to create directory handle"); - goto end; - } else if (!S_ISDIR(stat_buf.st_mode)) { - char full_path[LTTNG_PATH_MAX]; - - /* Best effort for logging purposes. */ - ret = get_full_path(ref_handle, path, full_path, - sizeof(full_path)); - if (ret) { - full_path[0] = '\0'; - } - - ERR("Failed to initialize directory handle to \"%s\": not a directory", - full_path); - goto end; - } - if (!path) { - new_handle = lttng_directory_handle_copy(ref_handle); - goto end; - } - - path_len = strlen(path); - if (path_len == 0) { - ERR("Failed to initialize directory handle: provided path is an empty string"); - ret = -1; - goto end; - } - if (*path == '/') { - new_path = strdup(path); - if (!new_path) { - goto end; - } - /* Takes ownership of new_path. */ - new_handle = _lttng_directory_handle_create(new_path); - new_path = NULL; - goto end; - } - - add_trailing_slash = path[path_len - 1] != '/'; - - handle_path_len = strlen(ref_handle->base_path) + path_len + - !!add_trailing_slash; - if (handle_path_len >= LTTNG_PATH_MAX) { - ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)", - handle_path_len, LTTNG_PATH_MAX); - goto end; - } - new_path = zmalloc(handle_path_len); - if (!new_path) { - PERROR("Failed to initialize directory handle"); - goto end; - } - - ret = sprintf(new_handle->base_path, "%s%s%s", - ref_handle->base_path, - path, - add_trailing_slash ? "/" : ""); - if (ret == -1 || ret >= handle_path_len) { - ERR("Failed to initialize directory handle: path formatting failed"); - goto end; - } - new_handle = _lttng_directory_handle_create(new_path); - new_path = NULL; -end: - free(new_path); - return new_handle; -} - -struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd( - int dirfd) -{ - LTTNG_ASSERT(dirfd == AT_FDCWD); - return lttng_directory_handle_create(NULL); -} - -static -void lttng_directory_handle_release(struct urcu_ref *ref) -{ - struct lttng_directory_handle *handle = - container_of(ref, struct lttng_directory_handle, ref); - - free(handle->base_path); - lttng_directory_handle_invalidate(handle); - free(handle); -} - -struct lttng_directory_handle *lttng_directory_handle_copy( - const struct lttng_directory_handle *handle) -{ - struct lttng_directory_handle *new_handle = NULL; - char *new_path = NULL; - - if (handle->base_path) { - new_path = strdup(handle->base_path); - if (!new_path) { - goto end; - } - } - new_handle = _lttng_directory_handle_create(new_path); -end: - return new_handle; -} - -bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs, - const struct lttng_directory_handle *rhs) -{ - return strcmp(lhs->base_path, rhs->base_path) == 0; -} - -static -void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) -{ - handle->base_path = NULL; -} - -int lttng_directory_handle_stat(const struct lttng_directory_handle *handle, - const char *subdirectory, struct stat *st) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = stat(fullpath, st); -end: - return ret; -} - -bool lttng_directory_handle_uses_fd( - const struct lttng_directory_handle *handle) -{ - return false; -} - -static -int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle, - const char *subdirectory, mode_t mode) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = mkdir(fullpath, mode); -end: - return ret; -} - -static -int lttng_directory_handle_open(const struct lttng_directory_handle *handle, - const char *filename, int flags, mode_t mode) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = open(fullpath, flags, mode); -end: - return ret; -} - -static -int lttng_directory_handle_unlink( - const struct lttng_directory_handle *handle, - const char *filename) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = unlink(fullpath); -end: - return ret; -} - -static -int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path, - mode_t mode, uid_t uid, gid_t gid) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_mkdir(fullpath, mode, uid, gid); -end: - return ret; -} - -static -int _run_as_open(const struct lttng_directory_handle *handle, - const char *filename, - int flags, mode_t mode, uid_t uid, gid_t gid) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_open(fullpath, flags, mode, uid, gid); -end: - return ret; -} - -static -int _run_as_unlink(const struct lttng_directory_handle *handle, - const char *filename, uid_t uid, gid_t gid) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_unlink(fullpath, uid, gid); -end: - return ret; -} - -static -int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, - const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_mkdir_recursive(fullpath, mode, uid, gid); -end: - return ret; -} - -static -int _lttng_directory_handle_rename( - const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name) -{ - int ret; - char old_fullpath[LTTNG_PATH_MAX]; - char new_fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(old_handle, old_name, old_fullpath, - sizeof(old_fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - ret = get_full_path(new_handle, new_name, new_fullpath, - sizeof(new_fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = rename(old_fullpath, new_fullpath); -end: - return ret; -} - -static -int _run_as_rename(const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name, uid_t uid, gid_t gid) -{ - int ret; - char old_fullpath[LTTNG_PATH_MAX]; - char new_fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(old_handle, old_name, old_fullpath, - sizeof(old_fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - ret = get_full_path(new_handle, new_name, new_fullpath, - sizeof(new_fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_rename(old_fullpath, new_fullpath, uid, gid); -end: - return ret; -} - -static -DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, - const char *path) -{ - int ret; - DIR *dir_stream = NULL; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - dir_stream = opendir(fullpath); -end: - return dir_stream; -} - -static -int lttng_directory_handle_rmdir( - const struct lttng_directory_handle *handle, const char *name) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = rmdir(fullpath); -end: - return ret; -} - -static -int _run_as_rmdir(const struct lttng_directory_handle *handle, - const char *name, uid_t uid, gid_t gid) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_rmdir(fullpath, uid, gid); -end: - return ret; -} - -static -int _run_as_rmdir_recursive( - const struct lttng_directory_handle *handle, const char *name, - uid_t uid, gid_t gid, int flags) -{ - int ret; - char fullpath[LTTNG_PATH_MAX]; - - ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); - if (ret) { - errno = ENOMEM; - goto end; - } - - ret = run_as_rmdir_recursive(fullpath, uid, gid, flags); -end: - return ret; -} - -#endif /* HAVE_DIRFD */ - -/* Common implementation. */ - -/* - * On some filesystems (e.g. nfs), mkdir will validate access rights before - * checking for the existence of the path element. This means that on a setup - * where "/home/" is a mounted NFS share, and running as an unpriviledged user, - * recursively creating a path of the form "/home/my_user/trace/" will fail with - * EACCES on mkdir("/home", ...). - * - * Checking the path for existence allows us to work around this behaviour. - */ -static -int create_directory_check_exists(const struct lttng_directory_handle *handle, - const char *path, mode_t mode) -{ - int ret = 0; - struct stat st; - - ret = lttng_directory_handle_stat(handle, path, &st); - if (ret == 0) { - if (S_ISDIR(st.st_mode)) { - /* Directory exists, skip. */ - goto end; - } else { - /* Exists, but is not a directory. */ - errno = ENOTDIR; - ret = -1; - goto end; - } - } else if (errno != ENOENT) { - goto end; - } - - /* - * Let mkdir handle other errors as the caller expects mkdir - * semantics. - */ - ret = lttng_directory_handle_mkdir(handle, path, mode); -end: - return ret; -} - -static -int create_directory_recursive(const struct lttng_directory_handle *handle, - const char *path, mode_t mode) -{ - char *p, tmp[LTTNG_PATH_MAX]; - size_t len; - int ret; - - LTTNG_ASSERT(path); - - ret = lttng_strncpy(tmp, path, sizeof(tmp)); - if (ret) { - ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)", - strlen(path) + 1, sizeof(tmp)); - goto error; - } - - len = strlen(path); - if (tmp[len - 1] == '/') { - tmp[len - 1] = 0; - } - - for (p = tmp + 1; *p; p++) { - if (*p == '/') { - *p = 0; - if (tmp[strlen(tmp) - 1] == '.' && - tmp[strlen(tmp) - 2] == '.' && - tmp[strlen(tmp) - 3] == '/') { - ERR("Using '/../' is not permitted in the trace path (%s)", - tmp); - ret = -1; - goto error; - } - ret = create_directory_check_exists(handle, tmp, mode); - if (ret < 0) { - if (errno != EACCES) { - PERROR("Failed to create directory \"%s\"", - path); - ret = -errno; - goto error; - } - } - *p = '/'; - } - } - - ret = create_directory_check_exists(handle, tmp, mode); - if (ret < 0) { - PERROR("mkdirat recursive last element"); - ret = -errno; - } -error: - return ret; -} - -bool lttng_directory_handle_get(struct lttng_directory_handle *handle) -{ - return urcu_ref_get_unless_zero(&handle->ref); -} - -void lttng_directory_handle_put(struct lttng_directory_handle *handle) -{ - if (!handle) { - return; - } - LTTNG_ASSERT(handle->ref.refcount); - urcu_ref_put(&handle->ref, lttng_directory_handle_release); -} - -int lttng_directory_handle_create_subdirectory_as_user( - const struct lttng_directory_handle *handle, - const char *subdirectory, - mode_t mode, const struct lttng_credentials *creds) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = create_directory_check_exists(handle, - subdirectory, mode); - } else { - ret = _run_as_mkdir(handle, subdirectory, mode, - lttng_credentials_get_uid(creds), - lttng_credentials_get_gid(creds)); - } - - return ret; -} - -int lttng_directory_handle_create_subdirectory_recursive_as_user( - const struct lttng_directory_handle *handle, - const char *subdirectory_path, - mode_t mode, const struct lttng_credentials *creds) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = create_directory_recursive(handle, - subdirectory_path, mode); - } else { - ret = _run_as_mkdir_recursive(handle, subdirectory_path, - mode, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); - } - - return ret; -} - -int lttng_directory_handle_create_subdirectory( - const struct lttng_directory_handle *handle, - const char *subdirectory, - mode_t mode) -{ - return lttng_directory_handle_create_subdirectory_as_user( - handle, subdirectory, mode, NULL); -} - -int lttng_directory_handle_create_subdirectory_recursive( - const struct lttng_directory_handle *handle, - const char *subdirectory_path, - mode_t mode) -{ - return lttng_directory_handle_create_subdirectory_recursive_as_user( - handle, subdirectory_path, mode, NULL); -} - -int lttng_directory_handle_open_file_as_user( - const struct lttng_directory_handle *handle, - const char *filename, - int flags, mode_t mode, - const struct lttng_credentials *creds) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = lttng_directory_handle_open(handle, filename, flags, - mode); - } else { - ret = _run_as_open(handle, filename, flags, mode, - lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); - } - return ret; -} - -int lttng_directory_handle_open_file( - const struct lttng_directory_handle *handle, - const char *filename, - int flags, mode_t mode) -{ - return lttng_directory_handle_open_file_as_user(handle, filename, flags, - mode, NULL); -} - -int lttng_directory_handle_unlink_file_as_user( - const struct lttng_directory_handle *handle, - const char *filename, - const struct lttng_credentials *creds) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = lttng_directory_handle_unlink(handle, filename); - } else { - ret = _run_as_unlink(handle, filename, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); - } - return ret; -} - -int lttng_directory_handle_unlink_file( - const struct lttng_directory_handle *handle, - const char *filename) -{ - return lttng_directory_handle_unlink_file_as_user(handle, - filename, NULL); -} - -int lttng_directory_handle_rename( - const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name) -{ - return lttng_directory_handle_rename_as_user(old_handle, old_name, - new_handle, new_name, NULL); -} - -int lttng_directory_handle_rename_as_user( - const struct lttng_directory_handle *old_handle, - const char *old_name, - const struct lttng_directory_handle *new_handle, - const char *new_name, - const struct lttng_credentials *creds) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = _lttng_directory_handle_rename(old_handle, - old_name, new_handle, new_name); - } else { - ret = _run_as_rename(old_handle, old_name, new_handle, - new_name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); - } - return ret; -} - -int lttng_directory_handle_remove_subdirectory( - const struct lttng_directory_handle *handle, - const char *name) -{ - return lttng_directory_handle_remove_subdirectory_as_user(handle, name, - NULL); -} - -int lttng_directory_handle_remove_subdirectory_as_user( - const struct lttng_directory_handle *handle, - const char *name, - const struct lttng_credentials *creds) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = lttng_directory_handle_rmdir(handle, name); - } else { - ret = _run_as_rmdir(handle, name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); - } - return ret; -} - -struct rmdir_frame { - ssize_t parent_frame_idx; - DIR *dir; - bool empty; - /* Size including '\0'. */ - size_t path_size; -}; - -static -void rmdir_frame_fini(void *data) -{ - int ret; - struct rmdir_frame *frame = data; - - ret = closedir(frame->dir); - if (ret == -1) { - PERROR("Failed to close directory stream"); - } -} - -static -int remove_directory_recursive(const struct lttng_directory_handle *handle, - const char *path, int flags) -{ - int ret; - struct lttng_dynamic_array frames; - size_t current_frame_idx = 0; - struct rmdir_frame initial_frame = { - .parent_frame_idx = -1, - .dir = lttng_directory_handle_opendir(handle, path), - .empty = true, - .path_size = strlen(path) + 1, - }; - struct lttng_dynamic_buffer current_path; - const char separator = '/'; - - lttng_dynamic_buffer_init(¤t_path); - lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame), - rmdir_frame_fini); - - if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG | - LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) { - ERR("Unknown flags %d", flags); - ret = -1; - goto end; - } - - if (!initial_frame.dir) { - if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG && - errno == ENOENT) { - DBG("Cannot rmdir \"%s\": root does not exist", path); - ret = 0; - goto end; - } else { - PERROR("Failed to rmdir \"%s\"", path); - ret = -1; - goto end; - } - } - - ret = lttng_dynamic_array_add_element(&frames, &initial_frame); - if (ret) { - ERR("Failed to push context frame during recursive directory removal"); - rmdir_frame_fini(&initial_frame); - goto end; - } - - ret = lttng_dynamic_buffer_append( - ¤t_path, path, initial_frame.path_size); - if (ret) { - ERR("Failed to set initial path during recursive directory removal"); - ret = -1; - goto end; - } - - while (lttng_dynamic_array_get_count(&frames) > 0) { - struct dirent *entry; - struct rmdir_frame *current_frame = - lttng_dynamic_array_get_element( - &frames, current_frame_idx); - - LTTNG_ASSERT(current_frame->dir); - ret = lttng_dynamic_buffer_set_size( - ¤t_path, current_frame->path_size); - LTTNG_ASSERT(!ret); - current_path.data[current_path.size - 1] = '\0'; - - while ((entry = readdir(current_frame->dir))) { - struct stat st; - - if (!strcmp(entry->d_name, ".") || - !strcmp(entry->d_name, "..")) { - continue; - } - - /* Set current_path to the entry's path. */ - ret = lttng_dynamic_buffer_set_size( - ¤t_path, current_path.size - 1); - LTTNG_ASSERT(!ret); - ret = lttng_dynamic_buffer_append(¤t_path, - &separator, sizeof(separator)); - if (ret) { - goto end; - } - ret = lttng_dynamic_buffer_append(¤t_path, - entry->d_name, - strlen(entry->d_name) + 1); - if (ret) { - goto end; - } - - if (lttng_directory_handle_stat( - handle, current_path.data, &st)) { - if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) && - errno == ENOENT) { - break; - } - PERROR("Failed to stat \"%s\"", - current_path.data); - ret = -1; - goto end; - } - - if (!S_ISDIR(st.st_mode)) { - if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) { - current_frame->empty = false; - break; - } else { - /* Not empty, abort. */ - DBG("Directory \"%s\" is not empty; refusing to remove directory", - current_path.data); - ret = -1; - goto end; - } - } else { - struct rmdir_frame new_frame = { - .path_size = current_path.size, - .dir = lttng_directory_handle_opendir( - handle, - current_path.data), - .empty = true, - .parent_frame_idx = current_frame_idx, - }; - - if (!new_frame.dir) { - if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG && - errno == ENOENT) { - DBG("Non-existing directory stream during recursive directory removal"); - break; - } else { - PERROR("Failed to open directory stream during recursive directory removal"); - ret = -1; - goto end; - } - } - ret = lttng_dynamic_array_add_element( - &frames, &new_frame); - if (ret) { - ERR("Failed to push context frame during recursive directory removal"); - rmdir_frame_fini(&new_frame); - goto end; - } - current_frame_idx++; - /* We break iteration on readdir. */ - break; - } - } - if (entry) { - continue; - } - - /* Pop rmdir frame. */ - if (current_frame->empty) { - ret = lttng_directory_handle_rmdir( - handle, current_path.data); - if (ret) { - if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) || - errno != ENOENT) { - PERROR("Failed to remove \"%s\" during recursive directory removal", - current_path.data); - goto end; - } - DBG("Non-existing directory stream during recursive directory removal"); - } - } else if (current_frame->parent_frame_idx >= 0) { - struct rmdir_frame *parent_frame; - - parent_frame = lttng_dynamic_array_get_element(&frames, - current_frame->parent_frame_idx); - LTTNG_ASSERT(parent_frame); - parent_frame->empty = false; - } - ret = lttng_dynamic_array_remove_element( - &frames, current_frame_idx); - if (ret) { - ERR("Failed to pop context frame during recursive directory removal"); - goto end; - } - current_frame_idx--; - } -end: - lttng_dynamic_array_reset(&frames); - lttng_dynamic_buffer_reset(¤t_path); - return ret; -} - -int lttng_directory_handle_remove_subdirectory_recursive( - const struct lttng_directory_handle *handle, - const char *name, - int flags) -{ - return lttng_directory_handle_remove_subdirectory_recursive_as_user( - handle, name, NULL, flags); -} - -int lttng_directory_handle_remove_subdirectory_recursive_as_user( - const struct lttng_directory_handle *handle, - const char *name, - const struct lttng_credentials *creds, - int flags) -{ - int ret; - - if (!creds) { - /* Run as current user. */ - ret = remove_directory_recursive(handle, name, flags); - } else { - ret = _run_as_rmdir_recursive(handle, name, lttng_credentials_get_uid(creds), - lttng_credentials_get_gid(creds), flags); - } - return ret; -} diff --git a/src/common/compat/directory-handle.cpp b/src/common/compat/directory-handle.cpp new file mode 100644 index 000000000..edd82ebb6 --- /dev/null +++ b/src/common/compat/directory-handle.cpp @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * This compatibility layer shares a common "base" that is implemented + * in terms of an internal API. This file contains two implementations + * of the internal API below. + */ +static +int lttng_directory_handle_mkdir( + const struct lttng_directory_handle *handle, + const char *path, mode_t mode); +static +int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path, + mode_t mode, uid_t uid, gid_t gid); +static +int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, + const char *path, mode_t mode, uid_t uid, gid_t gid); +static +int lttng_directory_handle_open(const struct lttng_directory_handle *handle, + const char *filename, int flags, mode_t mode); +static +int _run_as_open(const struct lttng_directory_handle *handle, + const char *filename, + int flags, mode_t mode, uid_t uid, gid_t gid); +static +int lttng_directory_handle_unlink( + const struct lttng_directory_handle *handle, + const char *filename); +static +int _run_as_unlink(const struct lttng_directory_handle *handle, + const char *filename, uid_t uid, gid_t gid); +static +int _lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name); +static +int _run_as_rename(const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, uid_t uid, gid_t gid); +static +DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, + const char *path); +static +int lttng_directory_handle_rmdir( + const struct lttng_directory_handle *handle, const char *name); +static +int _run_as_rmdir(const struct lttng_directory_handle *handle, + const char *name, uid_t uid, gid_t gid); +static +int _run_as_rmdir_recursive( + const struct lttng_directory_handle *handle, const char *name, + uid_t uid, gid_t gid, int flags); +static +void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle); +static +void lttng_directory_handle_release(struct urcu_ref *ref); + +#ifdef HAVE_DIRFD + +/* + * Special inode number reserved to represent the "current working directory". + * ino_t is spec'ed as being an unsigned integral type. + */ +#define RESERVED_AT_FDCWD_INO \ + ({ \ + uint64_t reserved_val; \ + switch (sizeof(ino_t)) { \ + case 4: \ + reserved_val = UINT32_MAX; \ + break; \ + case 8: \ + reserved_val = UINT64_MAX; \ + break; \ + default: \ + abort(); \ + } \ + (ino_t) reserved_val; \ + }) + +struct lttng_directory_handle *lttng_directory_handle_create(const char *path) +{ + lttng_directory_handle cwd_handle {}; + cwd_handle.dirfd = AT_FDCWD; + + /* Open a handle to the CWD if NULL is passed. */ + return lttng_directory_handle_create_from_handle(path, &cwd_handle); +} + +struct lttng_directory_handle *lttng_directory_handle_create_from_handle( + const char *path, + const struct lttng_directory_handle *ref_handle) +{ + int dirfd; + struct lttng_directory_handle *handle = NULL; + + if (!path) { + handle = lttng_directory_handle_copy(ref_handle); + goto end; + } + if (!*path) { + ERR("Failed to initialize directory handle: provided path is an empty string"); + goto end; + } + + dirfd = openat(ref_handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dirfd == -1) { + PERROR("Failed to initialize directory handle to \"%s\"", path); + goto end; + } + + handle = lttng_directory_handle_create_from_dirfd(dirfd); + if (!handle) { + goto error_close; + } +end: + return handle; +error_close: + if (close(dirfd)) { + PERROR("Failed to close directory file descriptor"); + } + return NULL; +} + +struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd( + int dirfd) +{ + int ret; + struct lttng_directory_handle *handle = (lttng_directory_handle *) zmalloc(sizeof(*handle)); + struct stat stat_buf; + + if (!handle) { + goto end; + } + + if (dirfd != AT_FDCWD) { + ret = fstat(dirfd, &stat_buf); + if (ret) { + PERROR("Failed to fstat directory file descriptor %i", dirfd); + lttng_directory_handle_release(&handle->ref); + handle = NULL; + goto end; + } + } else { + handle->directory_inode = RESERVED_AT_FDCWD_INO; + } + handle->dirfd = dirfd; + urcu_ref_init(&handle->ref); +end: + return handle; +} + +static +void lttng_directory_handle_release(struct urcu_ref *ref) +{ + int ret; + struct lttng_directory_handle *handle = + container_of(ref, struct lttng_directory_handle, ref); + + if (handle->destroy_cb) { + handle->destroy_cb(handle, handle->destroy_cb_data); + } + + if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) { + goto end; + } + ret = close(handle->dirfd); + if (ret == -1) { + PERROR("Failed to close directory file descriptor of directory handle"); + } +end: + lttng_directory_handle_invalidate(handle); + free(handle); +} + +struct lttng_directory_handle *lttng_directory_handle_copy( + const struct lttng_directory_handle *handle) +{ + struct lttng_directory_handle *new_handle = NULL; + + if (handle->dirfd == AT_FDCWD) { + new_handle = lttng_directory_handle_create_from_dirfd(AT_FDCWD); + } else { + const int new_dirfd = dup(handle->dirfd); + + if (new_dirfd == -1) { + PERROR("Failed to duplicate directory file descriptor of directory handle"); + goto end; + } + new_handle = lttng_directory_handle_create_from_dirfd( + new_dirfd); + if (!new_handle && close(new_dirfd)) { + PERROR("Failed to close directory file descriptor of directory handle"); + } + } +end: + return new_handle; +} + +bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs, + const struct lttng_directory_handle *rhs) +{ + return lhs->directory_inode == rhs->directory_inode; +} + +static +void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) +{ + handle->dirfd = -1; +} + +int lttng_directory_handle_stat(const struct lttng_directory_handle *handle, + const char *path, struct stat *st) +{ + return fstatat(handle->dirfd, path, st, 0); +} + +bool lttng_directory_handle_uses_fd( + const struct lttng_directory_handle *handle) +{ + return handle->dirfd != AT_FDCWD; +} + +static +int lttng_directory_handle_mkdir( + const struct lttng_directory_handle *handle, + const char *path, mode_t mode) +{ + return mkdirat(handle->dirfd, path, mode); +} + +static +int lttng_directory_handle_open(const struct lttng_directory_handle *handle, + const char *filename, int flags, mode_t mode) +{ + return openat(handle->dirfd, filename, flags, mode); +} + +static +int _run_as_open(const struct lttng_directory_handle *handle, + const char *filename, + int flags, mode_t mode, uid_t uid, gid_t gid) +{ + return run_as_openat(handle->dirfd, filename, flags, mode, uid, gid); +} + +static +int _run_as_unlink(const struct lttng_directory_handle *handle, + const char *filename, uid_t uid, gid_t gid) +{ + return run_as_unlinkat(handle->dirfd, filename, uid, gid); +} + +static +int lttng_directory_handle_unlink( + const struct lttng_directory_handle *handle, + const char *filename) +{ + return unlinkat(handle->dirfd, filename, 0); +} + +static +int _run_as_mkdir(const struct lttng_directory_handle *handle, + const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + return run_as_mkdirat(handle->dirfd, path, mode, uid, gid); +} + +static +int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, + const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid); +} + +static +int _lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name) +{ + return renameat(old_handle->dirfd, old_name, + new_handle->dirfd, new_name); +} + +static +int _run_as_rename(const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, uid_t uid, gid_t gid) +{ + return run_as_renameat(old_handle->dirfd, old_name, new_handle->dirfd, + new_name, uid, gid); +} + +static +DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, + const char *path) +{ + DIR *dir_stream = NULL; + int fd = openat(handle->dirfd, path, O_RDONLY); + + if (fd < 0) { + goto end; + } + + dir_stream = fdopendir(fd); + if (!dir_stream) { + int ret; + + PERROR("Failed to open directory stream"); + ret = close(fd); + if (ret) { + PERROR("Failed to close file descriptor to %s", path); + } + goto end; + } + +end: + return dir_stream; +} + +static +int lttng_directory_handle_rmdir( + const struct lttng_directory_handle *handle, const char *name) +{ + int ret = unlinkat(handle->dirfd, name, AT_REMOVEDIR); + if (ret) { + PERROR("Failed to remove directory `%s`", name); + } + + return ret; +} + +static +int _run_as_rmdir(const struct lttng_directory_handle *handle, + const char *name, uid_t uid, gid_t gid) +{ + return run_as_rmdirat(handle->dirfd, name, uid, gid); +} + +static +int _run_as_rmdir_recursive( + const struct lttng_directory_handle *handle, const char *name, + uid_t uid, gid_t gid, int flags) +{ + return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid, flags); +} + +#else /* HAVE_DIRFD */ + +static +int get_full_path(const struct lttng_directory_handle *handle, + const char *subdirectory, char *fullpath, size_t size) +{ + int ret; + const bool subdirectory_is_absolute = + subdirectory && *subdirectory == '/'; + const char * const base = subdirectory_is_absolute ? + subdirectory : handle->base_path; + const char * const end = subdirectory && !subdirectory_is_absolute ? + subdirectory : NULL; + const size_t base_len = strlen(base); + const size_t end_len = end ? strlen(end) : 0; + const bool add_separator_slash = end && base[base_len - 1] != '/'; + const bool add_trailing_slash = end && end[end_len - 1] != '/'; + + ret = snprintf(fullpath, size, "%s%s%s%s", + base, + add_separator_slash ? "/" : "", + end ? end : "", + add_trailing_slash ? "/" : ""); + if (ret == -1 || ret >= size) { + ERR("Failed to format subdirectory from directory handle"); + ret = -1; + goto end; + } + ret = 0; +end: + return ret; +} + +static +struct lttng_directory_handle *_lttng_directory_handle_create(char *path) +{ + struct lttng_directory_handle *handle = zmalloc(sizeof(*handle)); + + if (!handle) { + goto end; + } + urcu_ref_init(&handle->ref); + handle->base_path = path; +end: + return handle; +} + +struct lttng_directory_handle *lttng_directory_handle_create( + const char *path) +{ + int ret; + const char *cwd = ""; + size_t cwd_len, path_len; + char cwd_buf[LTTNG_PATH_MAX] = {}; + char handle_buf[LTTNG_PATH_MAX] = {}; + struct lttng_directory_handle *new_handle = NULL; + bool add_cwd_slash = false, add_trailing_slash = false; + const struct lttng_directory_handle cwd_handle = { + .base_path = handle_buf, + }; + + path_len = path ? strlen(path) : 0; + add_trailing_slash = path && path[path_len - 1] != '/'; + if (!path || (path && *path != '/')) { + cwd = getcwd(cwd_buf, sizeof(cwd_buf)); + if (!cwd) { + PERROR("Failed to initialize directory handle, can't get current working directory"); + ret = -1; + goto end; + } + cwd_len = strlen(cwd); + if (cwd_len == 0) { + ERR("Failed to initialize directory handle, current working directory path has a length of 0"); + ret = -1; + goto end; + } + add_cwd_slash = cwd[cwd_len - 1] != '/'; + } + + ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s", + cwd, + add_cwd_slash ? "/" : "", + path ? : "", + add_trailing_slash ? "/" : ""); + if (ret == -1 || ret >= LTTNG_PATH_MAX) { + ERR("Failed to initialize directory handle, failed to format directory path"); + goto end; + } + + new_handle = lttng_directory_handle_create_from_handle(path, &cwd_handle); +end: + return new_handle; +} + +struct lttng_directory_handle *lttng_directory_handle_create_from_handle( + const char *path, + const struct lttng_directory_handle *ref_handle) +{ + int ret; + size_t path_len, handle_path_len; + bool add_trailing_slash; + struct stat stat_buf; + struct lttng_directory_handle *new_handle = NULL; + char *new_path = NULL; + + LTTNG_ASSERT(ref_handle && ref_handle->base_path); + + ret = lttng_directory_handle_stat(ref_handle, path, &stat_buf); + if (ret == -1) { + PERROR("Failed to create directory handle"); + goto end; + } else if (!S_ISDIR(stat_buf.st_mode)) { + char full_path[LTTNG_PATH_MAX]; + + /* Best effort for logging purposes. */ + ret = get_full_path(ref_handle, path, full_path, + sizeof(full_path)); + if (ret) { + full_path[0] = '\0'; + } + + ERR("Failed to initialize directory handle to \"%s\": not a directory", + full_path); + goto end; + } + if (!path) { + new_handle = lttng_directory_handle_copy(ref_handle); + goto end; + } + + path_len = strlen(path); + if (path_len == 0) { + ERR("Failed to initialize directory handle: provided path is an empty string"); + ret = -1; + goto end; + } + if (*path == '/') { + new_path = strdup(path); + if (!new_path) { + goto end; + } + /* Takes ownership of new_path. */ + new_handle = _lttng_directory_handle_create(new_path); + new_path = NULL; + goto end; + } + + add_trailing_slash = path[path_len - 1] != '/'; + + handle_path_len = strlen(ref_handle->base_path) + path_len + + !!add_trailing_slash; + if (handle_path_len >= LTTNG_PATH_MAX) { + ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)", + handle_path_len, LTTNG_PATH_MAX); + goto end; + } + new_path = zmalloc(handle_path_len); + if (!new_path) { + PERROR("Failed to initialize directory handle"); + goto end; + } + + ret = sprintf(new_handle->base_path, "%s%s%s", + ref_handle->base_path, + path, + add_trailing_slash ? "/" : ""); + if (ret == -1 || ret >= handle_path_len) { + ERR("Failed to initialize directory handle: path formatting failed"); + goto end; + } + new_handle = _lttng_directory_handle_create(new_path); + new_path = NULL; +end: + free(new_path); + return new_handle; +} + +struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd( + int dirfd) +{ + LTTNG_ASSERT(dirfd == AT_FDCWD); + return lttng_directory_handle_create(NULL); +} + +static +void lttng_directory_handle_release(struct urcu_ref *ref) +{ + struct lttng_directory_handle *handle = + container_of(ref, struct lttng_directory_handle, ref); + + free(handle->base_path); + lttng_directory_handle_invalidate(handle); + free(handle); +} + +struct lttng_directory_handle *lttng_directory_handle_copy( + const struct lttng_directory_handle *handle) +{ + struct lttng_directory_handle *new_handle = NULL; + char *new_path = NULL; + + if (handle->base_path) { + new_path = strdup(handle->base_path); + if (!new_path) { + goto end; + } + } + new_handle = _lttng_directory_handle_create(new_path); +end: + return new_handle; +} + +bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs, + const struct lttng_directory_handle *rhs) +{ + return strcmp(lhs->base_path, rhs->base_path) == 0; +} + +static +void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) +{ + handle->base_path = NULL; +} + +int lttng_directory_handle_stat(const struct lttng_directory_handle *handle, + const char *subdirectory, struct stat *st) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = stat(fullpath, st); +end: + return ret; +} + +bool lttng_directory_handle_uses_fd( + const struct lttng_directory_handle *handle) +{ + return false; +} + +static +int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle, + const char *subdirectory, mode_t mode) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = mkdir(fullpath, mode); +end: + return ret; +} + +static +int lttng_directory_handle_open(const struct lttng_directory_handle *handle, + const char *filename, int flags, mode_t mode) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = open(fullpath, flags, mode); +end: + return ret; +} + +static +int lttng_directory_handle_unlink( + const struct lttng_directory_handle *handle, + const char *filename) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = unlink(fullpath); +end: + return ret; +} + +static +int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path, + mode_t mode, uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_mkdir(fullpath, mode, uid, gid); +end: + return ret; +} + +static +int _run_as_open(const struct lttng_directory_handle *handle, + const char *filename, + int flags, mode_t mode, uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_open(fullpath, flags, mode, uid, gid); +end: + return ret; +} + +static +int _run_as_unlink(const struct lttng_directory_handle *handle, + const char *filename, uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, filename, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_unlink(fullpath, uid, gid); +end: + return ret; +} + +static +int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, + const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_mkdir_recursive(fullpath, mode, uid, gid); +end: + return ret; +} + +static +int _lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name) +{ + int ret; + char old_fullpath[LTTNG_PATH_MAX]; + char new_fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(old_handle, old_name, old_fullpath, + sizeof(old_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + ret = get_full_path(new_handle, new_name, new_fullpath, + sizeof(new_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = rename(old_fullpath, new_fullpath); +end: + return ret; +} + +static +int _run_as_rename(const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, uid_t uid, gid_t gid) +{ + int ret; + char old_fullpath[LTTNG_PATH_MAX]; + char new_fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(old_handle, old_name, old_fullpath, + sizeof(old_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + ret = get_full_path(new_handle, new_name, new_fullpath, + sizeof(new_fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_rename(old_fullpath, new_fullpath, uid, gid); +end: + return ret; +} + +static +DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle, + const char *path) +{ + int ret; + DIR *dir_stream = NULL; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, path, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + dir_stream = opendir(fullpath); +end: + return dir_stream; +} + +static +int lttng_directory_handle_rmdir( + const struct lttng_directory_handle *handle, const char *name) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = rmdir(fullpath); +end: + return ret; +} + +static +int _run_as_rmdir(const struct lttng_directory_handle *handle, + const char *name, uid_t uid, gid_t gid) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_rmdir(fullpath, uid, gid); +end: + return ret; +} + +static +int _run_as_rmdir_recursive( + const struct lttng_directory_handle *handle, const char *name, + uid_t uid, gid_t gid, int flags) +{ + int ret; + char fullpath[LTTNG_PATH_MAX]; + + ret = get_full_path(handle, name, fullpath, sizeof(fullpath)); + if (ret) { + errno = ENOMEM; + goto end; + } + + ret = run_as_rmdir_recursive(fullpath, uid, gid, flags); +end: + return ret; +} + +#endif /* HAVE_DIRFD */ + +/* Common implementation. */ + +/* + * On some filesystems (e.g. nfs), mkdir will validate access rights before + * checking for the existence of the path element. This means that on a setup + * where "/home/" is a mounted NFS share, and running as an unpriviledged user, + * recursively creating a path of the form "/home/my_user/trace/" will fail with + * EACCES on mkdir("/home", ...). + * + * Checking the path for existence allows us to work around this behaviour. + */ +static +int create_directory_check_exists(const struct lttng_directory_handle *handle, + const char *path, mode_t mode) +{ + int ret = 0; + struct stat st; + + ret = lttng_directory_handle_stat(handle, path, &st); + if (ret == 0) { + if (S_ISDIR(st.st_mode)) { + /* Directory exists, skip. */ + goto end; + } else { + /* Exists, but is not a directory. */ + errno = ENOTDIR; + ret = -1; + goto end; + } + } else if (errno != ENOENT) { + goto end; + } + + /* + * Let mkdir handle other errors as the caller expects mkdir + * semantics. + */ + ret = lttng_directory_handle_mkdir(handle, path, mode); +end: + return ret; +} + +static +int create_directory_recursive(const struct lttng_directory_handle *handle, + const char *path, mode_t mode) +{ + char *p, tmp[LTTNG_PATH_MAX]; + size_t len; + int ret; + + LTTNG_ASSERT(path); + + ret = lttng_strncpy(tmp, path, sizeof(tmp)); + if (ret) { + ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)", + strlen(path) + 1, sizeof(tmp)); + goto error; + } + + len = strlen(path); + if (tmp[len - 1] == '/') { + tmp[len - 1] = 0; + } + + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + if (tmp[strlen(tmp) - 1] == '.' && + tmp[strlen(tmp) - 2] == '.' && + tmp[strlen(tmp) - 3] == '/') { + ERR("Using '/../' is not permitted in the trace path (%s)", + tmp); + ret = -1; + goto error; + } + ret = create_directory_check_exists(handle, tmp, mode); + if (ret < 0) { + if (errno != EACCES) { + PERROR("Failed to create directory \"%s\"", + path); + ret = -errno; + goto error; + } + } + *p = '/'; + } + } + + ret = create_directory_check_exists(handle, tmp, mode); + if (ret < 0) { + PERROR("mkdirat recursive last element"); + ret = -errno; + } +error: + return ret; +} + +bool lttng_directory_handle_get(struct lttng_directory_handle *handle) +{ + return urcu_ref_get_unless_zero(&handle->ref); +} + +void lttng_directory_handle_put(struct lttng_directory_handle *handle) +{ + if (!handle) { + return; + } + LTTNG_ASSERT(handle->ref.refcount); + urcu_ref_put(&handle->ref, lttng_directory_handle_release); +} + +int lttng_directory_handle_create_subdirectory_as_user( + const struct lttng_directory_handle *handle, + const char *subdirectory, + mode_t mode, const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = create_directory_check_exists(handle, + subdirectory, mode); + } else { + ret = _run_as_mkdir(handle, subdirectory, mode, + lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds)); + } + + return ret; +} + +int lttng_directory_handle_create_subdirectory_recursive_as_user( + const struct lttng_directory_handle *handle, + const char *subdirectory_path, + mode_t mode, const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = create_directory_recursive(handle, + subdirectory_path, mode); + } else { + ret = _run_as_mkdir_recursive(handle, subdirectory_path, + mode, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); + } + + return ret; +} + +int lttng_directory_handle_create_subdirectory( + const struct lttng_directory_handle *handle, + const char *subdirectory, + mode_t mode) +{ + return lttng_directory_handle_create_subdirectory_as_user( + handle, subdirectory, mode, NULL); +} + +int lttng_directory_handle_create_subdirectory_recursive( + const struct lttng_directory_handle *handle, + const char *subdirectory_path, + mode_t mode) +{ + return lttng_directory_handle_create_subdirectory_recursive_as_user( + handle, subdirectory_path, mode, NULL); +} + +int lttng_directory_handle_open_file_as_user( + const struct lttng_directory_handle *handle, + const char *filename, + int flags, mode_t mode, + const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = lttng_directory_handle_open(handle, filename, flags, + mode); + } else { + ret = _run_as_open(handle, filename, flags, mode, + lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); + } + return ret; +} + +int lttng_directory_handle_open_file( + const struct lttng_directory_handle *handle, + const char *filename, + int flags, mode_t mode) +{ + return lttng_directory_handle_open_file_as_user(handle, filename, flags, + mode, NULL); +} + +int lttng_directory_handle_unlink_file_as_user( + const struct lttng_directory_handle *handle, + const char *filename, + const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = lttng_directory_handle_unlink(handle, filename); + } else { + ret = _run_as_unlink(handle, filename, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); + } + return ret; +} + +int lttng_directory_handle_unlink_file( + const struct lttng_directory_handle *handle, + const char *filename) +{ + return lttng_directory_handle_unlink_file_as_user(handle, + filename, NULL); +} + +int lttng_directory_handle_rename( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name) +{ + return lttng_directory_handle_rename_as_user(old_handle, old_name, + new_handle, new_name, NULL); +} + +int lttng_directory_handle_rename_as_user( + const struct lttng_directory_handle *old_handle, + const char *old_name, + const struct lttng_directory_handle *new_handle, + const char *new_name, + const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = _lttng_directory_handle_rename(old_handle, + old_name, new_handle, new_name); + } else { + ret = _run_as_rename(old_handle, old_name, new_handle, + new_name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); + } + return ret; +} + +int lttng_directory_handle_remove_subdirectory( + const struct lttng_directory_handle *handle, + const char *name) +{ + return lttng_directory_handle_remove_subdirectory_as_user(handle, name, + NULL); +} + +int lttng_directory_handle_remove_subdirectory_as_user( + const struct lttng_directory_handle *handle, + const char *name, + const struct lttng_credentials *creds) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = lttng_directory_handle_rmdir(handle, name); + } else { + ret = _run_as_rmdir(handle, name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); + } + return ret; +} + +struct rmdir_frame { + ssize_t parent_frame_idx; + DIR *dir; + bool empty; + /* Size including '\0'. */ + size_t path_size; +}; + +static +void rmdir_frame_fini(void *data) +{ + int ret; + struct rmdir_frame *frame = (rmdir_frame *) data; + + ret = closedir(frame->dir); + if (ret == -1) { + PERROR("Failed to close directory stream"); + } +} + +static +int remove_directory_recursive(const struct lttng_directory_handle *handle, + const char *path, int flags) +{ + int ret; + struct lttng_dynamic_array frames; + size_t current_frame_idx = 0; + struct rmdir_frame initial_frame = { + .parent_frame_idx = -1, + .dir = lttng_directory_handle_opendir(handle, path), + .empty = true, + .path_size = strlen(path) + 1, + }; + struct lttng_dynamic_buffer current_path; + const char separator = '/'; + + lttng_dynamic_buffer_init(¤t_path); + lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame), + rmdir_frame_fini); + + if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG | + LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) { + ERR("Unknown flags %d", flags); + ret = -1; + goto end; + } + + if (!initial_frame.dir) { + if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG && + errno == ENOENT) { + DBG("Cannot rmdir \"%s\": root does not exist", path); + ret = 0; + goto end; + } else { + PERROR("Failed to rmdir \"%s\"", path); + ret = -1; + goto end; + } + } + + ret = lttng_dynamic_array_add_element(&frames, &initial_frame); + if (ret) { + ERR("Failed to push context frame during recursive directory removal"); + rmdir_frame_fini(&initial_frame); + goto end; + } + + ret = lttng_dynamic_buffer_append( + ¤t_path, path, initial_frame.path_size); + if (ret) { + ERR("Failed to set initial path during recursive directory removal"); + ret = -1; + goto end; + } + + while (lttng_dynamic_array_get_count(&frames) > 0) { + struct dirent *entry; + struct rmdir_frame *current_frame = + (rmdir_frame *) lttng_dynamic_array_get_element( + &frames, current_frame_idx); + + LTTNG_ASSERT(current_frame->dir); + ret = lttng_dynamic_buffer_set_size( + ¤t_path, current_frame->path_size); + LTTNG_ASSERT(!ret); + current_path.data[current_path.size - 1] = '\0'; + + while ((entry = readdir(current_frame->dir))) { + struct stat st; + + if (!strcmp(entry->d_name, ".") || + !strcmp(entry->d_name, "..")) { + continue; + } + + /* Set current_path to the entry's path. */ + ret = lttng_dynamic_buffer_set_size( + ¤t_path, current_path.size - 1); + LTTNG_ASSERT(!ret); + ret = lttng_dynamic_buffer_append(¤t_path, + &separator, sizeof(separator)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(¤t_path, + entry->d_name, + strlen(entry->d_name) + 1); + if (ret) { + goto end; + } + + if (lttng_directory_handle_stat( + handle, current_path.data, &st)) { + if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) && + errno == ENOENT) { + break; + } + PERROR("Failed to stat \"%s\"", + current_path.data); + ret = -1; + goto end; + } + + if (!S_ISDIR(st.st_mode)) { + if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) { + current_frame->empty = false; + break; + } else { + /* Not empty, abort. */ + DBG("Directory \"%s\" is not empty; refusing to remove directory", + current_path.data); + ret = -1; + goto end; + } + } else { + struct rmdir_frame new_frame = { + .parent_frame_idx = (ssize_t) current_frame_idx, + .dir = lttng_directory_handle_opendir( + handle, + current_path.data), + .empty = true, + .path_size = current_path.size, + }; + + if (!new_frame.dir) { + if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG && + errno == ENOENT) { + DBG("Non-existing directory stream during recursive directory removal"); + break; + } else { + PERROR("Failed to open directory stream during recursive directory removal"); + ret = -1; + goto end; + } + } + ret = lttng_dynamic_array_add_element( + &frames, &new_frame); + if (ret) { + ERR("Failed to push context frame during recursive directory removal"); + rmdir_frame_fini(&new_frame); + goto end; + } + current_frame_idx++; + /* We break iteration on readdir. */ + break; + } + } + if (entry) { + continue; + } + + /* Pop rmdir frame. */ + if (current_frame->empty) { + ret = lttng_directory_handle_rmdir( + handle, current_path.data); + if (ret) { + if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) || + errno != ENOENT) { + PERROR("Failed to remove \"%s\" during recursive directory removal", + current_path.data); + goto end; + } + DBG("Non-existing directory stream during recursive directory removal"); + } + } else if (current_frame->parent_frame_idx >= 0) { + struct rmdir_frame *parent_frame; + + parent_frame = (rmdir_frame *) lttng_dynamic_array_get_element(&frames, + current_frame->parent_frame_idx); + LTTNG_ASSERT(parent_frame); + parent_frame->empty = false; + } + ret = lttng_dynamic_array_remove_element( + &frames, current_frame_idx); + if (ret) { + ERR("Failed to pop context frame during recursive directory removal"); + goto end; + } + current_frame_idx--; + } +end: + lttng_dynamic_array_reset(&frames); + lttng_dynamic_buffer_reset(¤t_path); + return ret; +} + +int lttng_directory_handle_remove_subdirectory_recursive( + const struct lttng_directory_handle *handle, + const char *name, + int flags) +{ + return lttng_directory_handle_remove_subdirectory_recursive_as_user( + handle, name, NULL, flags); +} + +int lttng_directory_handle_remove_subdirectory_recursive_as_user( + const struct lttng_directory_handle *handle, + const char *name, + const struct lttng_credentials *creds, + int flags) +{ + int ret; + + if (!creds) { + /* Run as current user. */ + ret = remove_directory_recursive(handle, name, flags); + } else { + ret = _run_as_rmdir_recursive(handle, name, lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds), flags); + } + return ret; +} diff --git a/src/common/compat/fcntl.h b/src/common/compat/fcntl.h index b18e2f4e7..8bbdfe89c 100644 --- a/src/common/compat/fcntl.h +++ b/src/common/compat/fcntl.h @@ -13,6 +13,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + #if (defined(__CYGWIN__)) typedef long long off64_t; #endif @@ -64,4 +68,8 @@ static inline ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_ #error "Please add support for your OS." #endif /* __linux__ , __FreeBSD__, __CYGWIN__, __sun__, __APPLE__ */ +#ifdef __cplusplus +} +#endif + #endif /* _COMPAT_FCNTL_H */ diff --git a/src/common/compat/poll.c b/src/common/compat/poll.c deleted file mode 100644 index 9da2470d0..000000000 --- a/src/common/compat/poll.c +++ /dev/null @@ -1,719 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2019 Yannick Lamarre - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include - -#include -#include -#include -#include - -#include "poll.h" - -#if HAVE_EPOLL - -#include -#include -#include -#include -#include - -/* - * Maximum number of fd we can monitor. - * - * For epoll(7), /proc/sys/fs/epoll/max_user_watches (since Linux 2.6.28) will - * be used for the maximum size of the poll set. If this interface is not - * available, according to the manpage, the max_user_watches value is 1/25 (4%) - * of the available low memory divided by the registration cost in bytes which - * is 90 bytes on a 32-bit kernel and 160 bytes on a 64-bit kernel. - * - */ -static unsigned int poll_max_size; - -/* - * Resize the epoll events structure of the new size. - * - * Return 0 on success or else -1 with the current events pointer untouched. - */ -static int resize_poll_event(struct lttng_poll_event *events, - uint32_t new_size) -{ - struct epoll_event *ptr; - - LTTNG_ASSERT(events); - - ptr = realloc(events->events, new_size * sizeof(*ptr)); - if (ptr == NULL) { - PERROR("realloc epoll add"); - goto error; - } - if (new_size > events->alloc_size) { - /* Zero newly allocated memory */ - memset(ptr + events->alloc_size, 0, - (new_size - events->alloc_size) * sizeof(*ptr)); - } - events->events = ptr; - events->alloc_size = new_size; - - return 0; - -error: - return -1; -} - -/* - * Create epoll set and allocate returned events structure. - */ -int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) -{ - int ret; - - if (events == NULL || size <= 0) { - goto error; - } - - if (!poll_max_size) { - if (lttng_poll_set_max_size()) { - goto error; - } - } - - /* Don't bust the limit here */ - if (size > poll_max_size) { - size = poll_max_size; - } - - ret = compat_glibc_epoll_create(size, flags); - if (ret < 0) { - /* At this point, every error is fatal */ - PERROR("epoll_create1"); - goto error; - } - - events->epfd = ret; - - /* This *must* be freed by using lttng_poll_free() */ - events->events = zmalloc(size * sizeof(struct epoll_event)); - if (events->events == NULL) { - PERROR("zmalloc epoll set"); - goto error_close; - } - - events->alloc_size = events->init_size = size; - events->nb_fd = 0; - - return 0; - -error_close: - ret = close(events->epfd); - if (ret) { - PERROR("close"); - } -error: - return -1; -} - -/* - * Add a fd to the epoll set with requesting events. - */ -int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events) -{ - int ret; - struct epoll_event ev; - - if (events == NULL || events->events == NULL || fd < 0) { - ERR("Bad compat epoll add arguments"); - goto error; - } - - /* - * Zero struct epoll_event to ensure all representations of its - * union are zeroed. - */ - memset(&ev, 0, sizeof(ev)); - ev.events = req_events; - ev.data.fd = fd; - - ret = epoll_ctl(events->epfd, EPOLL_CTL_ADD, fd, &ev); - if (ret < 0) { - switch (errno) { - case EEXIST: - /* If exist, it's OK. */ - goto end; - case ENOSPC: - case EPERM: - /* Print PERROR and goto end not failing. Show must go on. */ - PERROR("epoll_ctl ADD"); - goto end; - default: - PERROR("epoll_ctl ADD fatal"); - goto error; - } - } - - events->nb_fd++; - -end: - return 0; - -error: - return -1; -} - -/* - * Remove a fd from the epoll set. - */ -int compat_epoll_del(struct lttng_poll_event *events, int fd) -{ - int ret; - - if (events == NULL || fd < 0 || events->nb_fd == 0) { - goto error; - } - - ret = epoll_ctl(events->epfd, EPOLL_CTL_DEL, fd, NULL); - if (ret < 0) { - switch (errno) { - case ENOENT: - case EPERM: - /* Print PERROR and goto end not failing. Show must go on. */ - PERROR("epoll_ctl DEL"); - goto end; - default: - PERROR("epoll_ctl DEL fatal"); - goto error; - } - } - - events->nb_fd--; - -end: - return 0; - -error: - return -1; -} - -/* - * Set an fd's events. - */ -int compat_epoll_mod(struct lttng_poll_event *events, int fd, uint32_t req_events) -{ - int ret; - struct epoll_event ev; - - if (events == NULL || fd < 0 || events->nb_fd == 0) { - goto error; - } - - /* - * Zero struct epoll_event to ensure all representations of its - * union are zeroed. - */ - memset(&ev, 0, sizeof(ev)); - ev.events = req_events; - ev.data.fd = fd; - - ret = epoll_ctl(events->epfd, EPOLL_CTL_MOD, fd, &ev); - if (ret < 0) { - switch (errno) { - case ENOENT: - case EPERM: - /* Print PERROR and goto end not failing. Show must go on. */ - PERROR("epoll_ctl MOD"); - goto end; - default: - PERROR("epoll_ctl MOD fatal"); - goto error; - } - } - -end: - return 0; - -error: - return -1; -} - -/* - * Wait on epoll set. This is a blocking call of timeout value. - */ -int compat_epoll_wait(struct lttng_poll_event *events, int timeout, - bool interruptible) -{ - int ret; - uint32_t new_size; - - if (events == NULL || events->events == NULL) { - ERR("Wrong arguments in compat_epoll_wait"); - goto error; - } - - if (events->nb_fd == 0) { - errno = EINVAL; - return -1; - } - - /* - * Resize if needed before waiting. We could either expand the array or - * shrink it down. It's important to note that after this step, we are - * ensured that the events argument of the epoll_wait call will be large - * enough to hold every possible returned events. - */ - new_size = 1U << utils_get_count_order_u32(events->nb_fd); - if (new_size != events->alloc_size && new_size >= events->init_size) { - ret = resize_poll_event(events, new_size); - if (ret < 0) { - /* ENOMEM problem at this point. */ - goto error; - } - } - - do { - ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout); - } while (!interruptible && ret == -1 && errno == EINTR); - if (ret < 0) { - if (errno != EINTR) { - PERROR("epoll_wait"); - } - goto error; - } - - /* - * Since the returned events are set sequentially in the "events" structure - * we only need to return the epoll_wait value and iterate over it. - */ - return ret; - -error: - return -1; -} - -/* - * Setup poll set maximum size. - */ -int compat_epoll_set_max_size(void) -{ - int ret, fd, retval = 0; - ssize_t size_ret; - char buf[64]; - - fd = open(COMPAT_EPOLL_PROC_PATH, O_RDONLY); - if (fd < 0) { - /* - * Failing on opening [1] is not an error per see. [1] was - * introduced in Linux 2.6.28 but epoll is available since - * 2.5.44. Hence, goto end and set a default value without - * setting an error return value. - * - * [1] /proc/sys/fs/epoll/max_user_watches - */ - retval = 0; - goto end; - } - - size_ret = lttng_read(fd, buf, sizeof(buf)); - /* - * Allow reading a file smaller than buf, but keep space for - * final \0. - */ - if (size_ret < 0 || size_ret >= sizeof(buf)) { - PERROR("read set max size"); - retval = -1; - goto end_read; - } - buf[size_ret] = '\0'; - poll_max_size = atoi(buf); -end_read: - ret = close(fd); - if (ret) { - PERROR("close"); - } -end: - if (!poll_max_size) { - poll_max_size = DEFAULT_POLL_SIZE; - } - DBG("epoll set max size is %d", poll_max_size); - return retval; -} - -#else /* HAVE_EPOLL */ - -#include -#include - -/* - * Maximum number of fd we can monitor. - * - * For poll(2), the max fds must not exceed RLIMIT_NOFILE given by - * getrlimit(2). - */ -static unsigned int poll_max_size; - -/* - * Resize the epoll events structure of the new size. - * - * Return 0 on success or else -1 with the current events pointer untouched. - */ -static int resize_poll_event(struct compat_poll_event_array *array, - uint32_t new_size) -{ - struct pollfd *ptr; - - LTTNG_ASSERT(array); - - /* Refuse to resize the array more than the max size. */ - if (new_size > poll_max_size) { - goto error; - } - - ptr = realloc(array->events, new_size * sizeof(*ptr)); - if (ptr == NULL) { - PERROR("realloc epoll add"); - goto error; - } - if (new_size > array->alloc_size) { - /* Zero newly allocated memory */ - memset(ptr + array->alloc_size, 0, - (new_size - array->alloc_size) * sizeof(*ptr)); - } - array->events = ptr; - array->alloc_size = new_size; - - return 0; - -error: - return -1; -} - -/* - * Update events with the current events object. - */ -static int update_current_events(struct lttng_poll_event *events) -{ - int ret; - struct compat_poll_event_array *current, *wait; - - LTTNG_ASSERT(events); - - current = &events->current; - wait = &events->wait; - - wait->nb_fd = current->nb_fd; - if (current->alloc_size != wait->alloc_size) { - ret = resize_poll_event(wait, current->alloc_size); - if (ret < 0) { - goto error; - } - } - memcpy(wait->events, current->events, - current->nb_fd * sizeof(*current->events)); - - /* Update is done. */ - events->need_update = 0; - - return 0; - -error: - return -1; -} - -/* - * Create pollfd data structure. - */ -int compat_poll_create(struct lttng_poll_event *events, int size) -{ - struct compat_poll_event_array *current, *wait; - - if (events == NULL || size <= 0) { - ERR("Wrong arguments for poll create"); - goto error; - } - - if (!poll_max_size) { - if (lttng_poll_set_max_size()) { - goto error; - } - } - - /* Don't bust the limit here */ - if (size > poll_max_size) { - size = poll_max_size; - } - - /* Reset everything before begining the allocation. */ - memset(events, 0, sizeof(struct lttng_poll_event)); - - current = &events->current; - wait = &events->wait; - - /* This *must* be freed by using lttng_poll_free() */ - wait->events = zmalloc(size * sizeof(struct pollfd)); - if (wait->events == NULL) { - PERROR("zmalloc struct pollfd"); - goto error; - } - - wait->alloc_size = wait->init_size = size; - - current->events = zmalloc(size * sizeof(struct pollfd)); - if (current->events == NULL) { - PERROR("zmalloc struct current pollfd"); - goto error; - } - - current->alloc_size = current->init_size = size; - - return 0; - -error: - return -1; -} - -/* - * Add fd to pollfd data structure with requested events. - */ -int compat_poll_add(struct lttng_poll_event *events, int fd, - uint32_t req_events) -{ - int new_size, ret, i; - struct compat_poll_event_array *current; - - if (events == NULL || events->current.events == NULL || fd < 0) { - ERR("Bad compat poll add arguments"); - goto error; - } - - current = &events->current; - - /* Check if fd we are trying to add is already there. */ - for (i = 0; i < current->nb_fd; i++) { - if (current->events[i].fd == fd) { - errno = EEXIST; - goto error; - } - } - - /* Resize array if needed. */ - new_size = 1U << utils_get_count_order_u32(current->nb_fd + 1); - if (new_size != current->alloc_size && new_size >= current->init_size) { - ret = resize_poll_event(current, new_size); - if (ret < 0) { - goto error; - } - } - - current->events[current->nb_fd].fd = fd; - current->events[current->nb_fd].events = req_events; - current->nb_fd++; - events->need_update = 1; - - DBG("fd %d of %d added to pollfd", fd, current->nb_fd); - - return 0; - -error: - return -1; -} - -/* - * Modify an fd's events.. - */ -int compat_poll_mod(struct lttng_poll_event *events, int fd, - uint32_t req_events) -{ - int i; - struct compat_poll_event_array *current; - - if (events == NULL || events->current.nb_fd == 0 || - events->current.events == NULL || fd < 0) { - ERR("Bad compat poll mod arguments"); - goto error; - } - - current = &events->current; - - for (i = 0; i < current->nb_fd; i++) { - if (current->events[i].fd == fd) { - current->events[i].events = req_events; - events->need_update = 1; - break; - } - } - - /* - * The epoll flavor doesn't flag modifying a non-included FD as an - * error. - */ - - return 0; - -error: - return -1; -} - -/* - * Remove a fd from the pollfd structure. - */ -int compat_poll_del(struct lttng_poll_event *events, int fd) -{ - int i, count = 0, ret; - uint32_t new_size; - struct compat_poll_event_array *current; - - if (events == NULL || events->current.nb_fd == 0 || - events->current.events == NULL || fd < 0) { - goto error; - } - - /* Ease our life a bit. */ - current = &events->current; - - for (i = 0; i < current->nb_fd; i++) { - /* Don't put back the fd we want to delete */ - if (current->events[i].fd != fd) { - current->events[count].fd = current->events[i].fd; - current->events[count].events = current->events[i].events; - count++; - } - } - - /* The fd was not in our set, return no error as with epoll. */ - if (current->nb_fd == count) { - goto end; - } - - /* No fd duplicate should be ever added into array. */ - LTTNG_ASSERT(current->nb_fd - 1 == count); - current->nb_fd = count; - - /* Resize array if needed. */ - new_size = 1U << utils_get_count_order_u32(current->nb_fd); - if (new_size != current->alloc_size && new_size >= current->init_size - && current->nb_fd != 0) { - ret = resize_poll_event(current, new_size); - if (ret < 0) { - goto error; - } - } - - events->need_update = 1; - -end: - return 0; - -error: - return -1; -} - -/* - * Wait on poll() with timeout. Blocking call. - */ -int compat_poll_wait(struct lttng_poll_event *events, int timeout, - bool interruptible) -{ - int ret, active_fd_count; - size_t pos = 0, consecutive_entries = 0, non_idle_pos; - - if (events == NULL || events->current.events == NULL) { - ERR("poll wait arguments error"); - goto error; - } - - if (events->current.nb_fd == 0) { - /* Return an invalid error to be consistent with epoll. */ - errno = EINVAL; - events->wait.nb_fd = 0; - goto error; - } - - if (events->need_update) { - ret = update_current_events(events); - if (ret < 0) { - errno = ENOMEM; - goto error; - } - } - - do { - ret = poll(events->wait.events, events->wait.nb_fd, timeout); - } while (!interruptible && ret == -1 && errno == EINTR); - if (ret < 0) { - if (errno != EINTR) { - PERROR("poll wait"); - } - goto error; - } - - active_fd_count = ret; - - /* - * Move all active pollfd structs to the beginning of the - * array to emulate compat-epoll behaviour. - */ - if (active_fd_count == events->wait.nb_fd) { - goto end; - } - - while (consecutive_entries != active_fd_count) { - struct pollfd *current = &events->wait.events[pos]; - struct pollfd idle_entry; - - if (current->revents != 0) { - consecutive_entries++; - pos++; - continue; - } - - non_idle_pos = pos; - - /* Look for next non-idle entry. */ - while (events->wait.events[++non_idle_pos].revents == 0); - - /* Swap idle and non-idle entries. */ - idle_entry = *current; - *current = events->wait.events[non_idle_pos]; - events->wait.events[non_idle_pos] = idle_entry; - - consecutive_entries++; - pos++; - } -end: - return ret; - -error: - return -1; -} - -/* - * Setup poll set maximum size. - */ -int compat_poll_set_max_size(void) -{ - int ret, retval = 0; - struct rlimit lim; - - ret = getrlimit(RLIMIT_NOFILE, &lim); - if (ret < 0) { - PERROR("getrlimit poll RLIMIT_NOFILE"); - retval = -1; - goto end; - } - - poll_max_size = lim.rlim_cur; -end: - if (poll_max_size == 0) { - poll_max_size = DEFAULT_POLL_SIZE; - } - DBG("poll set max size set to %u", poll_max_size); - return retval; -} - -#endif /* !HAVE_EPOLL */ diff --git a/src/common/compat/poll.cpp b/src/common/compat/poll.cpp new file mode 100644 index 000000000..928867ffb --- /dev/null +++ b/src/common/compat/poll.cpp @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2019 Yannick Lamarre + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include + +#include +#include +#include +#include + +#include "poll.h" + +#if HAVE_EPOLL + +#include +#include +#include +#include +#include + +/* + * Maximum number of fd we can monitor. + * + * For epoll(7), /proc/sys/fs/epoll/max_user_watches (since Linux 2.6.28) will + * be used for the maximum size of the poll set. If this interface is not + * available, according to the manpage, the max_user_watches value is 1/25 (4%) + * of the available low memory divided by the registration cost in bytes which + * is 90 bytes on a 32-bit kernel and 160 bytes on a 64-bit kernel. + * + */ +static unsigned int poll_max_size; + +/* + * Resize the epoll events structure of the new size. + * + * Return 0 on success or else -1 with the current events pointer untouched. + */ +static int resize_poll_event(struct lttng_poll_event *events, + uint32_t new_size) +{ + struct epoll_event *ptr; + + LTTNG_ASSERT(events); + + ptr = (epoll_event *) realloc(events->events, new_size * sizeof(*ptr)); + if (ptr == NULL) { + PERROR("realloc epoll add"); + goto error; + } + if (new_size > events->alloc_size) { + /* Zero newly allocated memory */ + memset(ptr + events->alloc_size, 0, + (new_size - events->alloc_size) * sizeof(*ptr)); + } + events->events = ptr; + events->alloc_size = new_size; + + return 0; + +error: + return -1; +} + +/* + * Create epoll set and allocate returned events structure. + */ +int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) +{ + int ret; + + if (events == NULL || size <= 0) { + goto error; + } + + if (!poll_max_size) { + if (lttng_poll_set_max_size()) { + goto error; + } + } + + /* Don't bust the limit here */ + if (size > poll_max_size) { + size = poll_max_size; + } + + ret = compat_glibc_epoll_create(size, flags); + if (ret < 0) { + /* At this point, every error is fatal */ + PERROR("epoll_create1"); + goto error; + } + + events->epfd = ret; + + /* This *must* be freed by using lttng_poll_free() */ + events->events = (epoll_event *) zmalloc(size * sizeof(struct epoll_event)); + if (events->events == NULL) { + PERROR("zmalloc epoll set"); + goto error_close; + } + + events->alloc_size = events->init_size = size; + events->nb_fd = 0; + + return 0; + +error_close: + ret = close(events->epfd); + if (ret) { + PERROR("close"); + } +error: + return -1; +} + +/* + * Add a fd to the epoll set with requesting events. + */ +int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events) +{ + int ret; + struct epoll_event ev; + + if (events == NULL || events->events == NULL || fd < 0) { + ERR("Bad compat epoll add arguments"); + goto error; + } + + /* + * Zero struct epoll_event to ensure all representations of its + * union are zeroed. + */ + memset(&ev, 0, sizeof(ev)); + ev.events = req_events; + ev.data.fd = fd; + + ret = epoll_ctl(events->epfd, EPOLL_CTL_ADD, fd, &ev); + if (ret < 0) { + switch (errno) { + case EEXIST: + /* If exist, it's OK. */ + goto end; + case ENOSPC: + case EPERM: + /* Print PERROR and goto end not failing. Show must go on. */ + PERROR("epoll_ctl ADD"); + goto end; + default: + PERROR("epoll_ctl ADD fatal"); + goto error; + } + } + + events->nb_fd++; + +end: + return 0; + +error: + return -1; +} + +/* + * Remove a fd from the epoll set. + */ +int compat_epoll_del(struct lttng_poll_event *events, int fd) +{ + int ret; + + if (events == NULL || fd < 0 || events->nb_fd == 0) { + goto error; + } + + ret = epoll_ctl(events->epfd, EPOLL_CTL_DEL, fd, NULL); + if (ret < 0) { + switch (errno) { + case ENOENT: + case EPERM: + /* Print PERROR and goto end not failing. Show must go on. */ + PERROR("epoll_ctl DEL"); + goto end; + default: + PERROR("epoll_ctl DEL fatal"); + goto error; + } + } + + events->nb_fd--; + +end: + return 0; + +error: + return -1; +} + +/* + * Set an fd's events. + */ +int compat_epoll_mod(struct lttng_poll_event *events, int fd, uint32_t req_events) +{ + int ret; + struct epoll_event ev; + + if (events == NULL || fd < 0 || events->nb_fd == 0) { + goto error; + } + + /* + * Zero struct epoll_event to ensure all representations of its + * union are zeroed. + */ + memset(&ev, 0, sizeof(ev)); + ev.events = req_events; + ev.data.fd = fd; + + ret = epoll_ctl(events->epfd, EPOLL_CTL_MOD, fd, &ev); + if (ret < 0) { + switch (errno) { + case ENOENT: + case EPERM: + /* Print PERROR and goto end not failing. Show must go on. */ + PERROR("epoll_ctl MOD"); + goto end; + default: + PERROR("epoll_ctl MOD fatal"); + goto error; + } + } + +end: + return 0; + +error: + return -1; +} + +/* + * Wait on epoll set. This is a blocking call of timeout value. + */ +int compat_epoll_wait(struct lttng_poll_event *events, int timeout, + bool interruptible) +{ + int ret; + uint32_t new_size; + + if (events == NULL || events->events == NULL) { + ERR("Wrong arguments in compat_epoll_wait"); + goto error; + } + + if (events->nb_fd == 0) { + errno = EINVAL; + return -1; + } + + /* + * Resize if needed before waiting. We could either expand the array or + * shrink it down. It's important to note that after this step, we are + * ensured that the events argument of the epoll_wait call will be large + * enough to hold every possible returned events. + */ + new_size = 1U << utils_get_count_order_u32(events->nb_fd); + if (new_size != events->alloc_size && new_size >= events->init_size) { + ret = resize_poll_event(events, new_size); + if (ret < 0) { + /* ENOMEM problem at this point. */ + goto error; + } + } + + do { + ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout); + } while (!interruptible && ret == -1 && errno == EINTR); + if (ret < 0) { + if (errno != EINTR) { + PERROR("epoll_wait"); + } + goto error; + } + + /* + * Since the returned events are set sequentially in the "events" structure + * we only need to return the epoll_wait value and iterate over it. + */ + return ret; + +error: + return -1; +} + +/* + * Setup poll set maximum size. + */ +int compat_epoll_set_max_size(void) +{ + int ret, fd, retval = 0; + ssize_t size_ret; + char buf[64]; + + fd = open(COMPAT_EPOLL_PROC_PATH, O_RDONLY); + if (fd < 0) { + /* + * Failing on opening [1] is not an error per see. [1] was + * introduced in Linux 2.6.28 but epoll is available since + * 2.5.44. Hence, goto end and set a default value without + * setting an error return value. + * + * [1] /proc/sys/fs/epoll/max_user_watches + */ + retval = 0; + goto end; + } + + size_ret = lttng_read(fd, buf, sizeof(buf)); + /* + * Allow reading a file smaller than buf, but keep space for + * final \0. + */ + if (size_ret < 0 || size_ret >= sizeof(buf)) { + PERROR("read set max size"); + retval = -1; + goto end_read; + } + buf[size_ret] = '\0'; + poll_max_size = atoi(buf); +end_read: + ret = close(fd); + if (ret) { + PERROR("close"); + } +end: + if (!poll_max_size) { + poll_max_size = DEFAULT_POLL_SIZE; + } + DBG("epoll set max size is %d", poll_max_size); + return retval; +} + +#else /* HAVE_EPOLL */ + +#include +#include + +/* + * Maximum number of fd we can monitor. + * + * For poll(2), the max fds must not exceed RLIMIT_NOFILE given by + * getrlimit(2). + */ +static unsigned int poll_max_size; + +/* + * Resize the epoll events structure of the new size. + * + * Return 0 on success or else -1 with the current events pointer untouched. + */ +static int resize_poll_event(struct compat_poll_event_array *array, + uint32_t new_size) +{ + struct pollfd *ptr; + + LTTNG_ASSERT(array); + + /* Refuse to resize the array more than the max size. */ + if (new_size > poll_max_size) { + goto error; + } + + ptr = realloc(array->events, new_size * sizeof(*ptr)); + if (ptr == NULL) { + PERROR("realloc epoll add"); + goto error; + } + if (new_size > array->alloc_size) { + /* Zero newly allocated memory */ + memset(ptr + array->alloc_size, 0, + (new_size - array->alloc_size) * sizeof(*ptr)); + } + array->events = ptr; + array->alloc_size = new_size; + + return 0; + +error: + return -1; +} + +/* + * Update events with the current events object. + */ +static int update_current_events(struct lttng_poll_event *events) +{ + int ret; + struct compat_poll_event_array *current, *wait; + + LTTNG_ASSERT(events); + + current = &events->current; + wait = &events->wait; + + wait->nb_fd = current->nb_fd; + if (current->alloc_size != wait->alloc_size) { + ret = resize_poll_event(wait, current->alloc_size); + if (ret < 0) { + goto error; + } + } + memcpy(wait->events, current->events, + current->nb_fd * sizeof(*current->events)); + + /* Update is done. */ + events->need_update = 0; + + return 0; + +error: + return -1; +} + +/* + * Create pollfd data structure. + */ +int compat_poll_create(struct lttng_poll_event *events, int size) +{ + struct compat_poll_event_array *current, *wait; + + if (events == NULL || size <= 0) { + ERR("Wrong arguments for poll create"); + goto error; + } + + if (!poll_max_size) { + if (lttng_poll_set_max_size()) { + goto error; + } + } + + /* Don't bust the limit here */ + if (size > poll_max_size) { + size = poll_max_size; + } + + /* Reset everything before begining the allocation. */ + memset(events, 0, sizeof(struct lttng_poll_event)); + + current = &events->current; + wait = &events->wait; + + /* This *must* be freed by using lttng_poll_free() */ + wait->events = zmalloc(size * sizeof(struct pollfd)); + if (wait->events == NULL) { + PERROR("zmalloc struct pollfd"); + goto error; + } + + wait->alloc_size = wait->init_size = size; + + current->events = zmalloc(size * sizeof(struct pollfd)); + if (current->events == NULL) { + PERROR("zmalloc struct current pollfd"); + goto error; + } + + current->alloc_size = current->init_size = size; + + return 0; + +error: + return -1; +} + +/* + * Add fd to pollfd data structure with requested events. + */ +int compat_poll_add(struct lttng_poll_event *events, int fd, + uint32_t req_events) +{ + int new_size, ret, i; + struct compat_poll_event_array *current; + + if (events == NULL || events->current.events == NULL || fd < 0) { + ERR("Bad compat poll add arguments"); + goto error; + } + + current = &events->current; + + /* Check if fd we are trying to add is already there. */ + for (i = 0; i < current->nb_fd; i++) { + if (current->events[i].fd == fd) { + errno = EEXIST; + goto error; + } + } + + /* Resize array if needed. */ + new_size = 1U << utils_get_count_order_u32(current->nb_fd + 1); + if (new_size != current->alloc_size && new_size >= current->init_size) { + ret = resize_poll_event(current, new_size); + if (ret < 0) { + goto error; + } + } + + current->events[current->nb_fd].fd = fd; + current->events[current->nb_fd].events = req_events; + current->nb_fd++; + events->need_update = 1; + + DBG("fd %d of %d added to pollfd", fd, current->nb_fd); + + return 0; + +error: + return -1; +} + +/* + * Modify an fd's events.. + */ +int compat_poll_mod(struct lttng_poll_event *events, int fd, + uint32_t req_events) +{ + int i; + struct compat_poll_event_array *current; + + if (events == NULL || events->current.nb_fd == 0 || + events->current.events == NULL || fd < 0) { + ERR("Bad compat poll mod arguments"); + goto error; + } + + current = &events->current; + + for (i = 0; i < current->nb_fd; i++) { + if (current->events[i].fd == fd) { + current->events[i].events = req_events; + events->need_update = 1; + break; + } + } + + /* + * The epoll flavor doesn't flag modifying a non-included FD as an + * error. + */ + + return 0; + +error: + return -1; +} + +/* + * Remove a fd from the pollfd structure. + */ +int compat_poll_del(struct lttng_poll_event *events, int fd) +{ + int i, count = 0, ret; + uint32_t new_size; + struct compat_poll_event_array *current; + + if (events == NULL || events->current.nb_fd == 0 || + events->current.events == NULL || fd < 0) { + goto error; + } + + /* Ease our life a bit. */ + current = &events->current; + + for (i = 0; i < current->nb_fd; i++) { + /* Don't put back the fd we want to delete */ + if (current->events[i].fd != fd) { + current->events[count].fd = current->events[i].fd; + current->events[count].events = current->events[i].events; + count++; + } + } + + /* The fd was not in our set, return no error as with epoll. */ + if (current->nb_fd == count) { + goto end; + } + + /* No fd duplicate should be ever added into array. */ + LTTNG_ASSERT(current->nb_fd - 1 == count); + current->nb_fd = count; + + /* Resize array if needed. */ + new_size = 1U << utils_get_count_order_u32(current->nb_fd); + if (new_size != current->alloc_size && new_size >= current->init_size + && current->nb_fd != 0) { + ret = resize_poll_event(current, new_size); + if (ret < 0) { + goto error; + } + } + + events->need_update = 1; + +end: + return 0; + +error: + return -1; +} + +/* + * Wait on poll() with timeout. Blocking call. + */ +int compat_poll_wait(struct lttng_poll_event *events, int timeout, + bool interruptible) +{ + int ret, active_fd_count; + size_t pos = 0, consecutive_entries = 0, non_idle_pos; + + if (events == NULL || events->current.events == NULL) { + ERR("poll wait arguments error"); + goto error; + } + + if (events->current.nb_fd == 0) { + /* Return an invalid error to be consistent with epoll. */ + errno = EINVAL; + events->wait.nb_fd = 0; + goto error; + } + + if (events->need_update) { + ret = update_current_events(events); + if (ret < 0) { + errno = ENOMEM; + goto error; + } + } + + do { + ret = poll(events->wait.events, events->wait.nb_fd, timeout); + } while (!interruptible && ret == -1 && errno == EINTR); + if (ret < 0) { + if (errno != EINTR) { + PERROR("poll wait"); + } + goto error; + } + + active_fd_count = ret; + + /* + * Move all active pollfd structs to the beginning of the + * array to emulate compat-epoll behaviour. + */ + if (active_fd_count == events->wait.nb_fd) { + goto end; + } + + while (consecutive_entries != active_fd_count) { + struct pollfd *current = &events->wait.events[pos]; + struct pollfd idle_entry; + + if (current->revents != 0) { + consecutive_entries++; + pos++; + continue; + } + + non_idle_pos = pos; + + /* Look for next non-idle entry. */ + while (events->wait.events[++non_idle_pos].revents == 0); + + /* Swap idle and non-idle entries. */ + idle_entry = *current; + *current = events->wait.events[non_idle_pos]; + events->wait.events[non_idle_pos] = idle_entry; + + consecutive_entries++; + pos++; + } +end: + return ret; + +error: + return -1; +} + +/* + * Setup poll set maximum size. + */ +int compat_poll_set_max_size(void) +{ + int ret, retval = 0; + struct rlimit lim; + + ret = getrlimit(RLIMIT_NOFILE, &lim); + if (ret < 0) { + PERROR("getrlimit poll RLIMIT_NOFILE"); + retval = -1; + goto end; + } + + poll_max_size = lim.rlim_cur; +end: + if (poll_max_size == 0) { + poll_max_size = DEFAULT_POLL_SIZE; + } + DBG("poll set max size set to %u", poll_max_size); + return retval; +} + +#endif /* !HAVE_EPOLL */ diff --git a/src/common/filter-grammar-test.c b/src/common/filter-grammar-test.c deleted file mode 100644 index bd8f2de7f..000000000 --- a/src/common/filter-grammar-test.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * filter-grammar-test.c - * - * LTTng filter grammar test - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -int main(int argc, char **argv) -{ - struct filter_parser_ctx *ctx; - int ret; - int print_xml = 0, generate_ir = 0, generate_bytecode = 0, - print_bytecode = 0; - int argidx; - - for (argidx = 1; argidx < argc; argidx++) { - if (strcmp(argv[argidx], "-p") == 0) - print_xml = 1; - else if (strcmp(argv[argidx], "-i") == 0) - generate_ir = 1; - else if (strcmp(argv[argidx], "-b") == 0) - generate_bytecode = 1; - else if (strcmp(argv[argidx], "-d") == 0) - filter_parser_debug = 1; - else if (strcmp(argv[argidx], "-B") == 0) - print_bytecode = 1; - } - - /* - * Force generate the bytecode if the user asks to print the bytecode - * (can't print it without generating it first). - */ - if (print_bytecode) { - generate_bytecode = 1; - } - - /* - * Force generate the IR if the user asks to generate the bytecode - * (the bytecode is generated by visiting the IR). - */ - if (generate_bytecode) { - generate_ir = 1; - } - - ctx = filter_parser_ctx_alloc(stdin); - if (!ctx) { - fprintf(stderr, "Error allocating parser\n"); - goto alloc_error; - } - ret = filter_parser_ctx_append_ast(ctx); - if (ret) { - fprintf(stderr, "Parse error\n"); - goto parse_error; - } - if (print_xml) { - ret = filter_visitor_print_xml(ctx, stdout, 0); - if (ret) { - fflush(stdout); - fprintf(stderr, "XML print error\n"); - goto parse_error; - } - } - if (generate_ir) { - printf("Generating IR... "); - fflush(stdout); - ret = filter_visitor_ir_generate(ctx); - if (ret) { - fprintf(stderr, "Generate IR error\n"); - goto parse_error; - } - printf("done\n"); - - printf("Validating IR... "); - fflush(stdout); - ret = filter_visitor_ir_check_binary_op_nesting(ctx); - if (ret) { - goto parse_error; - } - printf("done\n"); - } - if (generate_bytecode) { - printf("Generating bytecode... "); - fflush(stdout); - ret = filter_visitor_bytecode_generate(ctx); - if (ret) { - fprintf(stderr, "Generate bytecode error\n"); - goto parse_error; - } - printf("done\n"); - printf("Size of bytecode generated: %u bytes.\n", - bytecode_get_len(&ctx->bytecode->b)); - } - - if (print_bytecode) { - unsigned int bytecode_len, len, i; - - len = bytecode_get_len(&ctx->bytecode->b); - bytecode_len = ctx->bytecode->b.reloc_table_offset; - printf("Bytecode:\n"); - for (i = 0; i < bytecode_len; i++) { - printf("0x%X ", - ((uint8_t *) ctx->bytecode->b.data)[i]); - } - printf("\n"); - printf("Reloc table:\n"); - for (i = bytecode_len; i < len;) { - printf("{ 0x%X, ", - *(uint16_t *) &ctx->bytecode->b.data[i]); - i += sizeof(uint16_t); - printf("%s } ", &((char *) ctx->bytecode->b.data)[i]); - i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1; - } - printf("\n"); - } - - filter_bytecode_free(ctx); - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); - return 0; - -parse_error: - filter_bytecode_free(ctx); - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); -alloc_error: - exit(EXIT_FAILURE); -} diff --git a/src/common/filter-grammar-test.cpp b/src/common/filter-grammar-test.cpp new file mode 100644 index 000000000..bd8f2de7f --- /dev/null +++ b/src/common/filter-grammar-test.cpp @@ -0,0 +1,147 @@ +/* + * filter-grammar-test.c + * + * LTTng filter grammar test + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +int main(int argc, char **argv) +{ + struct filter_parser_ctx *ctx; + int ret; + int print_xml = 0, generate_ir = 0, generate_bytecode = 0, + print_bytecode = 0; + int argidx; + + for (argidx = 1; argidx < argc; argidx++) { + if (strcmp(argv[argidx], "-p") == 0) + print_xml = 1; + else if (strcmp(argv[argidx], "-i") == 0) + generate_ir = 1; + else if (strcmp(argv[argidx], "-b") == 0) + generate_bytecode = 1; + else if (strcmp(argv[argidx], "-d") == 0) + filter_parser_debug = 1; + else if (strcmp(argv[argidx], "-B") == 0) + print_bytecode = 1; + } + + /* + * Force generate the bytecode if the user asks to print the bytecode + * (can't print it without generating it first). + */ + if (print_bytecode) { + generate_bytecode = 1; + } + + /* + * Force generate the IR if the user asks to generate the bytecode + * (the bytecode is generated by visiting the IR). + */ + if (generate_bytecode) { + generate_ir = 1; + } + + ctx = filter_parser_ctx_alloc(stdin); + if (!ctx) { + fprintf(stderr, "Error allocating parser\n"); + goto alloc_error; + } + ret = filter_parser_ctx_append_ast(ctx); + if (ret) { + fprintf(stderr, "Parse error\n"); + goto parse_error; + } + if (print_xml) { + ret = filter_visitor_print_xml(ctx, stdout, 0); + if (ret) { + fflush(stdout); + fprintf(stderr, "XML print error\n"); + goto parse_error; + } + } + if (generate_ir) { + printf("Generating IR... "); + fflush(stdout); + ret = filter_visitor_ir_generate(ctx); + if (ret) { + fprintf(stderr, "Generate IR error\n"); + goto parse_error; + } + printf("done\n"); + + printf("Validating IR... "); + fflush(stdout); + ret = filter_visitor_ir_check_binary_op_nesting(ctx); + if (ret) { + goto parse_error; + } + printf("done\n"); + } + if (generate_bytecode) { + printf("Generating bytecode... "); + fflush(stdout); + ret = filter_visitor_bytecode_generate(ctx); + if (ret) { + fprintf(stderr, "Generate bytecode error\n"); + goto parse_error; + } + printf("done\n"); + printf("Size of bytecode generated: %u bytes.\n", + bytecode_get_len(&ctx->bytecode->b)); + } + + if (print_bytecode) { + unsigned int bytecode_len, len, i; + + len = bytecode_get_len(&ctx->bytecode->b); + bytecode_len = ctx->bytecode->b.reloc_table_offset; + printf("Bytecode:\n"); + for (i = 0; i < bytecode_len; i++) { + printf("0x%X ", + ((uint8_t *) ctx->bytecode->b.data)[i]); + } + printf("\n"); + printf("Reloc table:\n"); + for (i = bytecode_len; i < len;) { + printf("{ 0x%X, ", + *(uint16_t *) &ctx->bytecode->b.data[i]); + i += sizeof(uint16_t); + printf("%s } ", &((char *) ctx->bytecode->b.data)[i]); + i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1; + } + printf("\n"); + } + + filter_bytecode_free(ctx); + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); + return 0; + +parse_error: + filter_bytecode_free(ctx); + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); +alloc_error: + exit(EXIT_FAILURE); +} diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index e1e538657..a311bc3e4 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -78,7 +78,7 @@ TESTS += test_ust_data endif # URI unit tests -test_uri_SOURCES = test_uri.c +test_uri_SOURCES = test_uri.cpp test_uri_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBHASHTABLE) $(DL_LIBS) RELAYD_OBJS = $(top_builddir)/src/bin/lttng-relayd/backward-compatibility-group-by.$(OBJEXT) @@ -103,46 +103,46 @@ test_kernel_data_LDADD = $(LIBTAP) $(LIBLTTNG_SESSIOND_COMMON) $(DL_LIBS) # utils suffix for unit test # parse_size_suffix unit test -test_utils_parse_size_suffix_SOURCES = test_utils_parse_size_suffix.c +test_utils_parse_size_suffix_SOURCES = test_utils_parse_size_suffix.cpp test_utils_parse_size_suffix_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) # parse_time_suffix unit test -test_utils_parse_time_suffix_SOURCES = test_utils_parse_time_suffix.c +test_utils_parse_time_suffix_SOURCES = test_utils_parse_time_suffix.cpp test_utils_parse_time_suffix_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) # compat_poll unit test -test_utils_compat_poll_SOURCES = test_utils_compat_poll.c +test_utils_compat_poll_SOURCES = test_utils_compat_poll.cpp test_utils_compat_poll_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(DL_LIBS) \ $(top_builddir)/src/common/compat/libcompat.la $(LIBCOMMON) # compat_pthread unit test -test_utils_compat_pthread_SOURCES = test_utils_compat_pthread.c +test_utils_compat_pthread_SOURCES = test_utils_compat_pthread.cpp test_utils_compat_pthread_LDADD = $(LIBTAP) \ $(top_builddir)/src/common/compat/libcompat.la $(LIBCOMMON) # expand_path unit test -test_utils_expand_path_SOURCES = test_utils_expand_path.c +test_utils_expand_path_SOURCES = test_utils_expand_path.cpp test_utils_expand_path_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) # directory handle unit test -test_directory_handle_SOURCES = test_directory_handle.c +test_directory_handle_SOURCES = test_directory_handle.cpp test_directory_handle_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) # string utilities unit test -test_string_utils_SOURCES = test_string_utils.c +test_string_utils_SOURCES = test_string_utils.cpp test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS) # Notification api -test_notification_SOURCES = test_notification.c +test_notification_SOURCES = test_notification.cpp test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) # Event rule api -test_event_rule_SOURCES = test_event_rule.c +test_event_rule_SOURCES = test_event_rule.cpp test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) \ $(top_builddir)/src/bin/lttng/lttng-loglevel.$(OBJEXT) # Condition api -test_condition_SOURCES = test_condition.c +test_condition_SOURCES = test_condition.cpp test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # relayd backward compat for groou-by-session utilities @@ -151,42 +151,42 @@ test_relayd_backward_compat_group_by_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(RE test_relayd_backward_compat_group_by_session_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/bin/lttng-relayd # rate policy object unit test -test_rate_policy_SOURCES = test_rate_policy.c +test_rate_policy_SOURCES = test_rate_policy.cpp test_rate_policy_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) \ $(top_builddir)/src/bin/lttng/lttng-loglevel.$(OBJEXT) # fd tracker unit test -test_fd_tracker_SOURCES = test_fd_tracker.c +test_fd_tracker_SOURCES = test_fd_tracker.cpp test_fd_tracker_LDADD = $(LIBTAP) $(LIBFDTRACKER) $(DL_LIBS) $(URCU_LIBS) $(LIBCOMMON) $(LIBHASHTABLE) # uuid unit test -test_uuid_SOURCES = test_uuid.c +test_uuid_SOURCES = test_uuid.cpp test_uuid_LDADD = $(LIBTAP) $(LIBCOMMON) # buffer view unit test -test_buffer_view_SOURCES = test_buffer_view.c +test_buffer_view_SOURCES = test_buffer_view.cpp test_buffer_view_LDADD = $(LIBTAP) $(LIBCOMMON) # payload unit test -test_payload_SOURCES = test_payload.c +test_payload_SOURCES = test_payload.cpp test_payload_LDADD = $(LIBTAP) $(LIBSESSIOND_COMM) $(LIBCOMMON) # unix socket test -test_unix_socket_SOURCES = test_unix_socket.c +test_unix_socket_SOURCES = test_unix_socket.cpp test_unix_socket_LDADD = $(LIBTAP) $(LIBSESSIOND_COMM) $(LIBCOMMON) # Kernel probe location api test -test_kernel_probe_SOURCES = test_kernel_probe.c +test_kernel_probe_SOURCES = test_kernel_probe.cpp test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # # Event expression to bytecode test -test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.c +test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.cpp test_event_expr_to_bytecode_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(LIBCOMMON) # Log level rule api -test_log_level_rule_SOURCES = test_log_level_rule.c +test_log_level_rule_SOURCES = test_log_level_rule.cpp test_log_level_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # Action api -test_action_SOURCES = test_action.c +test_action_SOURCES = test_action.cpp test_action_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) diff --git a/tests/unit/ini_config/Makefile.am b/tests/unit/ini_config/Makefile.am index 7f15f99c9..003969775 100644 --- a/tests/unit/ini_config/Makefile.am +++ b/tests/unit/ini_config/Makefile.am @@ -10,7 +10,7 @@ LIBHASHTABLE=$(top_builddir)/src/common/hashtable/libhashtable.la noinst_PROGRAMS = ini_config EXTRA_DIST = test_ini_config sample.ini -ini_config_SOURCES = ini_config.c +ini_config_SOURCES = ini_config.cpp ini_config_LDADD = $(LIBTAP) $(LIBCONFIG) $(LIBCOMMON) $(LIBHASHTABLE) \ $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la diff --git a/tests/unit/ini_config/ini_config.c b/tests/unit/ini_config/ini_config.c deleted file mode 100644 index 6775ddd3f..000000000 --- a/tests/unit/ini_config/ini_config.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2013 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include - -struct state { - int section_1; - int section_2; - int section_3; - int section_global; - int text_entry; - int int_entry; -}; - -int lttng_opt_quiet = 1; -int lttng_opt_verbose = 0; -int lttng_opt_mi; - -static int entry_handler(const struct config_entry *entry, struct state *state) -{ - int ret = 0; - - if (!entry || !state) { - ret = -1; - goto end; - } - - if (!strcmp(entry->section, "section1")) { - state->section_1 = 1; - if (!strcmp(entry->name, "section1_entry") && - !strcmp(entry->value, "42")) { - state->int_entry = 1; - } - } - - if (!strcmp(entry->section, "section2")) { - state->section_2 = 1; - } - - if (!strcmp(entry->section, "section 3")) { - state->section_3 = 1; - if (!strcmp(entry->name, "name with a space") && - !strcmp(entry->value, "another value")) { - state->text_entry = 1; - } - } - - if (!strcmp(entry->section, "")) { - state->section_global = 1; - } -end: - return ret; -} - -int main(int argc, char **argv) -{ - char *path = NULL; - int ret; - struct state state = {}; - - if (argc < 2) { - diag("Usage: path_to_sample_INI_file"); - goto end; - } - - if (strlen(argv[1]) >= LTTNG_PATH_MAX) { - diag("The provided path exceeds the maximal permitted length of %i bytes", - LTTNG_PATH_MAX); - goto end; - } - path = utils_expand_path(argv[1]); - if (!path) { - fail("Failed to resolve sample INI file path"); - } - - plan_no_plan(); - ret = config_get_section_entries(path, NULL, - (config_entry_handler_cb)entry_handler, &state); - ok(ret == 0, "Successfully opened a config file, registered to all sections"); - ok(state.section_1 && state.section_2 && state.section_3 && - state.section_global, "Processed entries from each sections"); - ok(state.text_entry, "Text value parsed correctly"); - - memset(&state, 0, sizeof(struct state)); - ret = config_get_section_entries(path, "section1", - (config_entry_handler_cb)entry_handler, &state); - ok(ret == 0, "Successfully opened a config file, registered to one section"); - ok(state.section_1 && !state.section_2 && !state.section_3 && - !state.section_global, "Processed an entry from section1 only"); - ok(state.int_entry, "Int value parsed correctly"); - - memset(&state, 0, sizeof(struct state)); - ret = config_get_section_entries(path, "", - (config_entry_handler_cb)entry_handler, &state); - ok(ret == 0, "Successfully opened a config file, registered to the global section"); - ok(!state.section_1 && !state.section_2 && !state.section_3 && - state.section_global, "Processed an entry from the global section only"); -end: - free(path); - return exit_status(); -} diff --git a/tests/unit/ini_config/ini_config.cpp b/tests/unit/ini_config/ini_config.cpp new file mode 100644 index 000000000..6775ddd3f --- /dev/null +++ b/tests/unit/ini_config/ini_config.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include + +struct state { + int section_1; + int section_2; + int section_3; + int section_global; + int text_entry; + int int_entry; +}; + +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 0; +int lttng_opt_mi; + +static int entry_handler(const struct config_entry *entry, struct state *state) +{ + int ret = 0; + + if (!entry || !state) { + ret = -1; + goto end; + } + + if (!strcmp(entry->section, "section1")) { + state->section_1 = 1; + if (!strcmp(entry->name, "section1_entry") && + !strcmp(entry->value, "42")) { + state->int_entry = 1; + } + } + + if (!strcmp(entry->section, "section2")) { + state->section_2 = 1; + } + + if (!strcmp(entry->section, "section 3")) { + state->section_3 = 1; + if (!strcmp(entry->name, "name with a space") && + !strcmp(entry->value, "another value")) { + state->text_entry = 1; + } + } + + if (!strcmp(entry->section, "")) { + state->section_global = 1; + } +end: + return ret; +} + +int main(int argc, char **argv) +{ + char *path = NULL; + int ret; + struct state state = {}; + + if (argc < 2) { + diag("Usage: path_to_sample_INI_file"); + goto end; + } + + if (strlen(argv[1]) >= LTTNG_PATH_MAX) { + diag("The provided path exceeds the maximal permitted length of %i bytes", + LTTNG_PATH_MAX); + goto end; + } + path = utils_expand_path(argv[1]); + if (!path) { + fail("Failed to resolve sample INI file path"); + } + + plan_no_plan(); + ret = config_get_section_entries(path, NULL, + (config_entry_handler_cb)entry_handler, &state); + ok(ret == 0, "Successfully opened a config file, registered to all sections"); + ok(state.section_1 && state.section_2 && state.section_3 && + state.section_global, "Processed entries from each sections"); + ok(state.text_entry, "Text value parsed correctly"); + + memset(&state, 0, sizeof(struct state)); + ret = config_get_section_entries(path, "section1", + (config_entry_handler_cb)entry_handler, &state); + ok(ret == 0, "Successfully opened a config file, registered to one section"); + ok(state.section_1 && !state.section_2 && !state.section_3 && + !state.section_global, "Processed an entry from section1 only"); + ok(state.int_entry, "Int value parsed correctly"); + + memset(&state, 0, sizeof(struct state)); + ret = config_get_section_entries(path, "", + (config_entry_handler_cb)entry_handler, &state); + ok(ret == 0, "Successfully opened a config file, registered to the global section"); + ok(!state.section_1 && !state.section_2 && !state.section_3 && + state.section_global, "Processed an entry from the global section only"); +end: + free(path); + return exit_status(); +} diff --git a/tests/unit/test_action.c b/tests/unit/test_action.c deleted file mode 100644 index ad323e9ac..000000000 --- a/tests/unit/test_action.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - * test_action.c - * - * Unit tests for the notification API. - * - * Copyright (C) 2017 Jonathan Rajotte - * - * SPDX-License-Identifier: MIT - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 60 - -static void test_action_notify(void) -{ - int ret; - enum lttng_action_status status; - struct lttng_action *notify_action = NULL, - *notify_action_from_buffer = NULL; - struct lttng_rate_policy *policy = NULL, *default_policy; - struct lttng_payload payload; - - lttng_payload_init(&payload); - - /* To set. */ - policy = lttng_rate_policy_every_n_create(100); - /* For comparison. */ - default_policy = lttng_rate_policy_every_n_create(1); - - LTTNG_ASSERT(policy && default_policy); - - notify_action = lttng_action_notify_create(); - ok(notify_action, "Create notify action"); - ok(lttng_action_get_type(notify_action) == LTTNG_ACTION_TYPE_NOTIFY, - "Action has type LTTNG_ACTION_TYPE_NOTIFY"); - - /* Validate the default policy for a notify action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_notify_get_rate_policy( - notify_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - default_policy, - cur_policy), - "Default policy is every n=1"); - } - - /* Set a custom policy. */ - status = lttng_action_notify_set_rate_policy(notify_action, policy); - ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); - - /* Validate the custom policy for a notify action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_notify_get_rate_policy( - notify_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - policy, - cur_policy), - "Notify action policy get"); - } - - ret = lttng_action_serialize(notify_action, &payload); - ok(ret == 0, "Action notify serialized"); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - (void) lttng_action_create_from_payload( - &view, ¬ify_action_from_buffer); - } - ok(notify_action_from_buffer, - "Notify action created from payload is non-null"); - - ok(lttng_action_is_equal(notify_action, notify_action_from_buffer), - "Serialized and de-serialized notify action are equal"); - - lttng_rate_policy_destroy(default_policy); - lttng_rate_policy_destroy(policy); - lttng_action_destroy(notify_action); - lttng_action_destroy(notify_action_from_buffer); - lttng_payload_reset(&payload); -} - -static void test_action_rotate_session(void) -{ - int ret; - enum lttng_action_status status; - struct lttng_action *rotate_session_action = NULL, - *rotate_session_action_from_buffer = NULL; - struct lttng_rate_policy *policy = NULL, *default_policy; - struct lttng_payload payload; - const char *session_name = "my_session_name"; - const char *get_session_name; - - lttng_payload_init(&payload); - - /* To set. */ - policy = lttng_rate_policy_every_n_create(100); - /* For comparison. */ - default_policy = lttng_rate_policy_every_n_create(1); - - LTTNG_ASSERT(policy && default_policy); - - rotate_session_action = lttng_action_rotate_session_create(); - ok(rotate_session_action, "Create rotate_session action"); - ok(lttng_action_get_type(rotate_session_action) == - LTTNG_ACTION_TYPE_ROTATE_SESSION, - "Action has type LTTNG_ACTION_TYPE_ROTATE_SESSION"); - - /* Session name setter. */ - status = lttng_action_rotate_session_set_session_name(NULL, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,NULL) expect invalid"); - status = lttng_action_rotate_session_set_session_name( - rotate_session_action, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (object,NULL) expect invalid"); - status = lttng_action_rotate_session_set_session_name( - NULL, session_name); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,object) expect invalid"); - - /* Set the session name */ - status = lttng_action_rotate_session_set_session_name( - rotate_session_action, session_name); - ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); - - status = lttng_action_rotate_session_get_session_name( - rotate_session_action, &get_session_name); - ok(status == LTTNG_ACTION_STATUS_OK && - !strcmp(session_name, get_session_name), - "Get session name, expected `%s` got `%s`", - session_name, get_session_name); - - /* Validate the default policy for a rotate_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_rotate_session_get_rate_policy( - rotate_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - default_policy, - cur_policy), - "Default policy is every n=1"); - } - - /* Set a custom policy. */ - status = lttng_action_rotate_session_set_rate_policy( - rotate_session_action, policy); - ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); - - /* Validate the custom policy for a rotate_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_rotate_session_get_rate_policy( - rotate_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - policy, - cur_policy), - "rotate_session action policy get"); - } - - /* Ser/des tests. */ - ret = lttng_action_serialize(rotate_session_action, &payload); - ok(ret == 0, "Action rotate_session serialized"); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - (void) lttng_action_create_from_payload( - &view, &rotate_session_action_from_buffer); - } - ok(rotate_session_action_from_buffer, - "rotate_session action created from payload is non-null"); - - ok(lttng_action_is_equal(rotate_session_action, - rotate_session_action_from_buffer), - "Serialized and de-serialized rotate_session action are equal"); - - lttng_rate_policy_destroy(default_policy); - lttng_rate_policy_destroy(policy); - lttng_action_destroy(rotate_session_action); - lttng_action_destroy(rotate_session_action_from_buffer); - lttng_payload_reset(&payload); -} - -static void test_action_start_session(void) -{ - int ret; - enum lttng_action_status status; - struct lttng_action *start_session_action = NULL, - *start_session_action_from_buffer = NULL; - struct lttng_rate_policy *policy = NULL, *default_policy; - struct lttng_payload payload; - const char *session_name = "my_session_name"; - const char *get_session_name; - - lttng_payload_init(&payload); - - /* To set. */ - policy = lttng_rate_policy_every_n_create(100); - /* For comparison. */ - default_policy = lttng_rate_policy_every_n_create(1); - - LTTNG_ASSERT(policy && default_policy); - - start_session_action = lttng_action_start_session_create(); - ok(start_session_action, "Create start_session action"); - ok(lttng_action_get_type(start_session_action) == - LTTNG_ACTION_TYPE_START_SESSION, - "Action has type LTTNG_ACTION_TYPE_START_SESSION"); - - /* Session name setter. */ - status = lttng_action_start_session_set_session_name(NULL, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,NULL) expect invalid"); - status = lttng_action_start_session_set_session_name( - start_session_action, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (object,NULL) expect invalid"); - status = lttng_action_start_session_set_session_name( - NULL, session_name); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,object) expect invalid"); - - /* Set the session name */ - status = lttng_action_start_session_set_session_name( - start_session_action, session_name); - ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); - - status = lttng_action_start_session_get_session_name( - start_session_action, &get_session_name); - ok(status == LTTNG_ACTION_STATUS_OK && - !strcmp(session_name, get_session_name), - "Get session name, expected `%s` got `%s`", - session_name, get_session_name); - - /* Validate the default policy for a start_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_start_session_get_rate_policy( - start_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - default_policy, - cur_policy), - "Default policy is every n=1"); - } - - /* Set a custom policy. */ - status = lttng_action_start_session_set_rate_policy( - start_session_action, policy); - ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); - - /* Validate the custom policy for a start_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_start_session_get_rate_policy( - start_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - policy, - cur_policy), - "start_session action policy get"); - } - - /* Ser/des tests. */ - ret = lttng_action_serialize(start_session_action, &payload); - ok(ret == 0, "Action start_session serialized"); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - (void) lttng_action_create_from_payload( - &view, &start_session_action_from_buffer); - } - ok(start_session_action_from_buffer, - "start_session action created from payload is non-null"); - - ok(lttng_action_is_equal(start_session_action, - start_session_action_from_buffer), - "Serialized and de-serialized start_session action are equal"); - - lttng_rate_policy_destroy(default_policy); - lttng_rate_policy_destroy(policy); - lttng_action_destroy(start_session_action); - lttng_action_destroy(start_session_action_from_buffer); - lttng_payload_reset(&payload); -} - -static void test_action_stop_session(void) -{ - int ret; - enum lttng_action_status status; - struct lttng_action *stop_session_action = NULL, - *stop_session_action_from_buffer = NULL; - struct lttng_rate_policy *policy = NULL, *default_policy; - struct lttng_payload payload; - const char *session_name = "my_session_name"; - const char *get_session_name; - - lttng_payload_init(&payload); - - /* To set. */ - policy = lttng_rate_policy_every_n_create(100); - /* For comparison. */ - default_policy = lttng_rate_policy_every_n_create(1); - - LTTNG_ASSERT(policy && default_policy); - - stop_session_action = lttng_action_stop_session_create(); - ok(stop_session_action, "Create stop_session action"); - ok(lttng_action_get_type(stop_session_action) == - LTTNG_ACTION_TYPE_STOP_SESSION, - "Action has type LTTNG_ACTION_TYPE_STOP_SESSION"); - - /* Session name setter. */ - status = lttng_action_stop_session_set_session_name(NULL, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,NULL) expect invalid"); - status = lttng_action_stop_session_set_session_name( - stop_session_action, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (object,NULL) expect invalid"); - status = lttng_action_stop_session_set_session_name(NULL, session_name); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,object) expect invalid"); - - /* Set the session name */ - status = lttng_action_stop_session_set_session_name( - stop_session_action, session_name); - ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); - - status = lttng_action_stop_session_get_session_name( - stop_session_action, &get_session_name); - ok(status == LTTNG_ACTION_STATUS_OK && - !strcmp(session_name, get_session_name), - "Get session name, expected `%s` got `%s`", - session_name, get_session_name); - - /* Validate the default policy for a stop_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_stop_session_get_rate_policy( - stop_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - default_policy, - cur_policy), - "Default policy is every n=1"); - } - - /* Set a custom policy. */ - status = lttng_action_stop_session_set_rate_policy( - stop_session_action, policy); - ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); - - /* Validate the custom policy for a stop_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_stop_session_get_rate_policy( - stop_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - policy, - cur_policy), - "stop_session action policy get"); - } - - /* Ser/des tests. */ - ret = lttng_action_serialize(stop_session_action, &payload); - ok(ret == 0, "Action stop_session serialized"); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - (void) lttng_action_create_from_payload( - &view, &stop_session_action_from_buffer); - } - ok(stop_session_action_from_buffer, - "stop_session action created from payload is non-null"); - - ok(lttng_action_is_equal(stop_session_action, - stop_session_action_from_buffer), - "Serialized and de-serialized stop_session action are equal"); - - lttng_rate_policy_destroy(default_policy); - lttng_rate_policy_destroy(policy); - lttng_action_destroy(stop_session_action); - lttng_action_destroy(stop_session_action_from_buffer); - lttng_payload_reset(&payload); -} - -static void test_action_snapshot_session(void) -{ - int ret; - enum lttng_action_status status; - struct lttng_action *snapshot_session_action = NULL, - *snapshot_session_action_from_buffer = NULL; - struct lttng_rate_policy *policy = NULL, *default_policy; - struct lttng_payload payload; - const char *session_name = "my_session_name"; - const char *get_session_name; - - lttng_payload_init(&payload); - - /* To set. */ - policy = lttng_rate_policy_every_n_create(100); - /* For comparison. */ - default_policy = lttng_rate_policy_every_n_create(1); - - LTTNG_ASSERT(policy && default_policy); - - snapshot_session_action = lttng_action_snapshot_session_create(); - ok(snapshot_session_action, "Create snapshot_session action"); - ok(lttng_action_get_type(snapshot_session_action) == - LTTNG_ACTION_TYPE_SNAPSHOT_SESSION, - "Action has type LTTNG_ACTION_TYPE_SNAPSHOT_SESSION"); - - /* Session name setter. */ - status = lttng_action_snapshot_session_set_session_name(NULL, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,NULL) expect invalid"); - status = lttng_action_snapshot_session_set_session_name( - snapshot_session_action, NULL); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (object,NULL) expect invalid"); - status = lttng_action_snapshot_session_set_session_name( - NULL, session_name); - ok(status == LTTNG_ACTION_STATUS_INVALID, - "Set session name (NULL,object) expect invalid"); - - /* Set the session name */ - status = lttng_action_snapshot_session_set_session_name( - snapshot_session_action, session_name); - ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); - - status = lttng_action_snapshot_session_get_session_name( - snapshot_session_action, &get_session_name); - ok(status == LTTNG_ACTION_STATUS_OK && - !strcmp(session_name, get_session_name), - "Get session name, expected `%s` got `%s`", - session_name, get_session_name); - - /* Validate the default policy for a snapshot_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_snapshot_session_get_rate_policy( - snapshot_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - default_policy, - cur_policy), - "Default policy is every n=1"); - } - - /* Set a custom policy. */ - status = lttng_action_snapshot_session_set_rate_policy( - snapshot_session_action, policy); - ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); - - /* Validate the custom policy for a snapshot_session action. */ - { - const struct lttng_rate_policy *cur_policy = NULL; - status = lttng_action_snapshot_session_get_rate_policy( - snapshot_session_action, &cur_policy); - ok(status == LTTNG_ACTION_STATUS_OK && - lttng_rate_policy_is_equal( - policy, - cur_policy), - "snapshot_session action policy get"); - } - - /* Ser/des tests. */ - ret = lttng_action_serialize(snapshot_session_action, &payload); - ok(ret == 0, "Action snapshot_session serialized"); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - (void) lttng_action_create_from_payload( - &view, &snapshot_session_action_from_buffer); - } - ok(snapshot_session_action_from_buffer, - "snapshot_session action created from payload is non-null"); - - ok(lttng_action_is_equal(snapshot_session_action, - snapshot_session_action_from_buffer), - "Serialized and de-serialized snapshot_session action are equal"); - - lttng_rate_policy_destroy(default_policy); - lttng_rate_policy_destroy(policy); - lttng_action_destroy(snapshot_session_action); - lttng_action_destroy(snapshot_session_action_from_buffer); - lttng_payload_reset(&payload); -} - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_action_notify(); - test_action_rotate_session(); - test_action_start_session(); - test_action_stop_session(); - test_action_snapshot_session(); - return exit_status(); -} diff --git a/tests/unit/test_action.cpp b/tests/unit/test_action.cpp new file mode 100644 index 000000000..ad323e9ac --- /dev/null +++ b/tests/unit/test_action.cpp @@ -0,0 +1,540 @@ +/* + * test_action.c + * + * Unit tests for the notification API. + * + * Copyright (C) 2017 Jonathan Rajotte + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 60 + +static void test_action_notify(void) +{ + int ret; + enum lttng_action_status status; + struct lttng_action *notify_action = NULL, + *notify_action_from_buffer = NULL; + struct lttng_rate_policy *policy = NULL, *default_policy; + struct lttng_payload payload; + + lttng_payload_init(&payload); + + /* To set. */ + policy = lttng_rate_policy_every_n_create(100); + /* For comparison. */ + default_policy = lttng_rate_policy_every_n_create(1); + + LTTNG_ASSERT(policy && default_policy); + + notify_action = lttng_action_notify_create(); + ok(notify_action, "Create notify action"); + ok(lttng_action_get_type(notify_action) == LTTNG_ACTION_TYPE_NOTIFY, + "Action has type LTTNG_ACTION_TYPE_NOTIFY"); + + /* Validate the default policy for a notify action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_notify_get_rate_policy( + notify_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + default_policy, + cur_policy), + "Default policy is every n=1"); + } + + /* Set a custom policy. */ + status = lttng_action_notify_set_rate_policy(notify_action, policy); + ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); + + /* Validate the custom policy for a notify action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_notify_get_rate_policy( + notify_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + policy, + cur_policy), + "Notify action policy get"); + } + + ret = lttng_action_serialize(notify_action, &payload); + ok(ret == 0, "Action notify serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + (void) lttng_action_create_from_payload( + &view, ¬ify_action_from_buffer); + } + ok(notify_action_from_buffer, + "Notify action created from payload is non-null"); + + ok(lttng_action_is_equal(notify_action, notify_action_from_buffer), + "Serialized and de-serialized notify action are equal"); + + lttng_rate_policy_destroy(default_policy); + lttng_rate_policy_destroy(policy); + lttng_action_destroy(notify_action); + lttng_action_destroy(notify_action_from_buffer); + lttng_payload_reset(&payload); +} + +static void test_action_rotate_session(void) +{ + int ret; + enum lttng_action_status status; + struct lttng_action *rotate_session_action = NULL, + *rotate_session_action_from_buffer = NULL; + struct lttng_rate_policy *policy = NULL, *default_policy; + struct lttng_payload payload; + const char *session_name = "my_session_name"; + const char *get_session_name; + + lttng_payload_init(&payload); + + /* To set. */ + policy = lttng_rate_policy_every_n_create(100); + /* For comparison. */ + default_policy = lttng_rate_policy_every_n_create(1); + + LTTNG_ASSERT(policy && default_policy); + + rotate_session_action = lttng_action_rotate_session_create(); + ok(rotate_session_action, "Create rotate_session action"); + ok(lttng_action_get_type(rotate_session_action) == + LTTNG_ACTION_TYPE_ROTATE_SESSION, + "Action has type LTTNG_ACTION_TYPE_ROTATE_SESSION"); + + /* Session name setter. */ + status = lttng_action_rotate_session_set_session_name(NULL, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,NULL) expect invalid"); + status = lttng_action_rotate_session_set_session_name( + rotate_session_action, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (object,NULL) expect invalid"); + status = lttng_action_rotate_session_set_session_name( + NULL, session_name); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,object) expect invalid"); + + /* Set the session name */ + status = lttng_action_rotate_session_set_session_name( + rotate_session_action, session_name); + ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); + + status = lttng_action_rotate_session_get_session_name( + rotate_session_action, &get_session_name); + ok(status == LTTNG_ACTION_STATUS_OK && + !strcmp(session_name, get_session_name), + "Get session name, expected `%s` got `%s`", + session_name, get_session_name); + + /* Validate the default policy for a rotate_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_rotate_session_get_rate_policy( + rotate_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + default_policy, + cur_policy), + "Default policy is every n=1"); + } + + /* Set a custom policy. */ + status = lttng_action_rotate_session_set_rate_policy( + rotate_session_action, policy); + ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); + + /* Validate the custom policy for a rotate_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_rotate_session_get_rate_policy( + rotate_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + policy, + cur_policy), + "rotate_session action policy get"); + } + + /* Ser/des tests. */ + ret = lttng_action_serialize(rotate_session_action, &payload); + ok(ret == 0, "Action rotate_session serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + (void) lttng_action_create_from_payload( + &view, &rotate_session_action_from_buffer); + } + ok(rotate_session_action_from_buffer, + "rotate_session action created from payload is non-null"); + + ok(lttng_action_is_equal(rotate_session_action, + rotate_session_action_from_buffer), + "Serialized and de-serialized rotate_session action are equal"); + + lttng_rate_policy_destroy(default_policy); + lttng_rate_policy_destroy(policy); + lttng_action_destroy(rotate_session_action); + lttng_action_destroy(rotate_session_action_from_buffer); + lttng_payload_reset(&payload); +} + +static void test_action_start_session(void) +{ + int ret; + enum lttng_action_status status; + struct lttng_action *start_session_action = NULL, + *start_session_action_from_buffer = NULL; + struct lttng_rate_policy *policy = NULL, *default_policy; + struct lttng_payload payload; + const char *session_name = "my_session_name"; + const char *get_session_name; + + lttng_payload_init(&payload); + + /* To set. */ + policy = lttng_rate_policy_every_n_create(100); + /* For comparison. */ + default_policy = lttng_rate_policy_every_n_create(1); + + LTTNG_ASSERT(policy && default_policy); + + start_session_action = lttng_action_start_session_create(); + ok(start_session_action, "Create start_session action"); + ok(lttng_action_get_type(start_session_action) == + LTTNG_ACTION_TYPE_START_SESSION, + "Action has type LTTNG_ACTION_TYPE_START_SESSION"); + + /* Session name setter. */ + status = lttng_action_start_session_set_session_name(NULL, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,NULL) expect invalid"); + status = lttng_action_start_session_set_session_name( + start_session_action, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (object,NULL) expect invalid"); + status = lttng_action_start_session_set_session_name( + NULL, session_name); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,object) expect invalid"); + + /* Set the session name */ + status = lttng_action_start_session_set_session_name( + start_session_action, session_name); + ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); + + status = lttng_action_start_session_get_session_name( + start_session_action, &get_session_name); + ok(status == LTTNG_ACTION_STATUS_OK && + !strcmp(session_name, get_session_name), + "Get session name, expected `%s` got `%s`", + session_name, get_session_name); + + /* Validate the default policy for a start_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_start_session_get_rate_policy( + start_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + default_policy, + cur_policy), + "Default policy is every n=1"); + } + + /* Set a custom policy. */ + status = lttng_action_start_session_set_rate_policy( + start_session_action, policy); + ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); + + /* Validate the custom policy for a start_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_start_session_get_rate_policy( + start_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + policy, + cur_policy), + "start_session action policy get"); + } + + /* Ser/des tests. */ + ret = lttng_action_serialize(start_session_action, &payload); + ok(ret == 0, "Action start_session serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + (void) lttng_action_create_from_payload( + &view, &start_session_action_from_buffer); + } + ok(start_session_action_from_buffer, + "start_session action created from payload is non-null"); + + ok(lttng_action_is_equal(start_session_action, + start_session_action_from_buffer), + "Serialized and de-serialized start_session action are equal"); + + lttng_rate_policy_destroy(default_policy); + lttng_rate_policy_destroy(policy); + lttng_action_destroy(start_session_action); + lttng_action_destroy(start_session_action_from_buffer); + lttng_payload_reset(&payload); +} + +static void test_action_stop_session(void) +{ + int ret; + enum lttng_action_status status; + struct lttng_action *stop_session_action = NULL, + *stop_session_action_from_buffer = NULL; + struct lttng_rate_policy *policy = NULL, *default_policy; + struct lttng_payload payload; + const char *session_name = "my_session_name"; + const char *get_session_name; + + lttng_payload_init(&payload); + + /* To set. */ + policy = lttng_rate_policy_every_n_create(100); + /* For comparison. */ + default_policy = lttng_rate_policy_every_n_create(1); + + LTTNG_ASSERT(policy && default_policy); + + stop_session_action = lttng_action_stop_session_create(); + ok(stop_session_action, "Create stop_session action"); + ok(lttng_action_get_type(stop_session_action) == + LTTNG_ACTION_TYPE_STOP_SESSION, + "Action has type LTTNG_ACTION_TYPE_STOP_SESSION"); + + /* Session name setter. */ + status = lttng_action_stop_session_set_session_name(NULL, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,NULL) expect invalid"); + status = lttng_action_stop_session_set_session_name( + stop_session_action, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (object,NULL) expect invalid"); + status = lttng_action_stop_session_set_session_name(NULL, session_name); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,object) expect invalid"); + + /* Set the session name */ + status = lttng_action_stop_session_set_session_name( + stop_session_action, session_name); + ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); + + status = lttng_action_stop_session_get_session_name( + stop_session_action, &get_session_name); + ok(status == LTTNG_ACTION_STATUS_OK && + !strcmp(session_name, get_session_name), + "Get session name, expected `%s` got `%s`", + session_name, get_session_name); + + /* Validate the default policy for a stop_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_stop_session_get_rate_policy( + stop_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + default_policy, + cur_policy), + "Default policy is every n=1"); + } + + /* Set a custom policy. */ + status = lttng_action_stop_session_set_rate_policy( + stop_session_action, policy); + ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); + + /* Validate the custom policy for a stop_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_stop_session_get_rate_policy( + stop_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + policy, + cur_policy), + "stop_session action policy get"); + } + + /* Ser/des tests. */ + ret = lttng_action_serialize(stop_session_action, &payload); + ok(ret == 0, "Action stop_session serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + (void) lttng_action_create_from_payload( + &view, &stop_session_action_from_buffer); + } + ok(stop_session_action_from_buffer, + "stop_session action created from payload is non-null"); + + ok(lttng_action_is_equal(stop_session_action, + stop_session_action_from_buffer), + "Serialized and de-serialized stop_session action are equal"); + + lttng_rate_policy_destroy(default_policy); + lttng_rate_policy_destroy(policy); + lttng_action_destroy(stop_session_action); + lttng_action_destroy(stop_session_action_from_buffer); + lttng_payload_reset(&payload); +} + +static void test_action_snapshot_session(void) +{ + int ret; + enum lttng_action_status status; + struct lttng_action *snapshot_session_action = NULL, + *snapshot_session_action_from_buffer = NULL; + struct lttng_rate_policy *policy = NULL, *default_policy; + struct lttng_payload payload; + const char *session_name = "my_session_name"; + const char *get_session_name; + + lttng_payload_init(&payload); + + /* To set. */ + policy = lttng_rate_policy_every_n_create(100); + /* For comparison. */ + default_policy = lttng_rate_policy_every_n_create(1); + + LTTNG_ASSERT(policy && default_policy); + + snapshot_session_action = lttng_action_snapshot_session_create(); + ok(snapshot_session_action, "Create snapshot_session action"); + ok(lttng_action_get_type(snapshot_session_action) == + LTTNG_ACTION_TYPE_SNAPSHOT_SESSION, + "Action has type LTTNG_ACTION_TYPE_SNAPSHOT_SESSION"); + + /* Session name setter. */ + status = lttng_action_snapshot_session_set_session_name(NULL, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,NULL) expect invalid"); + status = lttng_action_snapshot_session_set_session_name( + snapshot_session_action, NULL); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (object,NULL) expect invalid"); + status = lttng_action_snapshot_session_set_session_name( + NULL, session_name); + ok(status == LTTNG_ACTION_STATUS_INVALID, + "Set session name (NULL,object) expect invalid"); + + /* Set the session name */ + status = lttng_action_snapshot_session_set_session_name( + snapshot_session_action, session_name); + ok(status == LTTNG_ACTION_STATUS_OK, "Set session name"); + + status = lttng_action_snapshot_session_get_session_name( + snapshot_session_action, &get_session_name); + ok(status == LTTNG_ACTION_STATUS_OK && + !strcmp(session_name, get_session_name), + "Get session name, expected `%s` got `%s`", + session_name, get_session_name); + + /* Validate the default policy for a snapshot_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_snapshot_session_get_rate_policy( + snapshot_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + default_policy, + cur_policy), + "Default policy is every n=1"); + } + + /* Set a custom policy. */ + status = lttng_action_snapshot_session_set_rate_policy( + snapshot_session_action, policy); + ok(status == LTTNG_ACTION_STATUS_OK, "Set rate policy"); + + /* Validate the custom policy for a snapshot_session action. */ + { + const struct lttng_rate_policy *cur_policy = NULL; + status = lttng_action_snapshot_session_get_rate_policy( + snapshot_session_action, &cur_policy); + ok(status == LTTNG_ACTION_STATUS_OK && + lttng_rate_policy_is_equal( + policy, + cur_policy), + "snapshot_session action policy get"); + } + + /* Ser/des tests. */ + ret = lttng_action_serialize(snapshot_session_action, &payload); + ok(ret == 0, "Action snapshot_session serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + (void) lttng_action_create_from_payload( + &view, &snapshot_session_action_from_buffer); + } + ok(snapshot_session_action_from_buffer, + "snapshot_session action created from payload is non-null"); + + ok(lttng_action_is_equal(snapshot_session_action, + snapshot_session_action_from_buffer), + "Serialized and de-serialized snapshot_session action are equal"); + + lttng_rate_policy_destroy(default_policy); + lttng_rate_policy_destroy(policy); + lttng_action_destroy(snapshot_session_action); + lttng_action_destroy(snapshot_session_action_from_buffer); + lttng_payload_reset(&payload); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_action_notify(); + test_action_rotate_session(); + test_action_start_session(); + test_action_stop_session(); + test_action_snapshot_session(); + return exit_status(); +} diff --git a/tests/unit/test_buffer_view.c b/tests/unit/test_buffer_view.c deleted file mode 100644 index a129d4a9a..000000000 --- a/tests/unit/test_buffer_view.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2020 EfficiOS, inc. - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - - -#include -#include - -static const int TEST_COUNT = 5; - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -static void test_contains_string(void) -{ - const char buf[] = {'A', 'l', 'l', 'o', '\0'}; - struct lttng_buffer_view view = lttng_buffer_view_init(buf, 0, 5); - struct lttng_buffer_view view_minus_one = - lttng_buffer_view_init(buf, 0, 4); - - ok1(!lttng_buffer_view_contains_string(&view, buf, 4)); - ok1(lttng_buffer_view_contains_string(&view, buf, 5)); - ok1(!lttng_buffer_view_contains_string(&view, buf, 6)); - - ok1(!lttng_buffer_view_contains_string(&view_minus_one, buf, 4)); - ok1(!lttng_buffer_view_contains_string(&view_minus_one, buf, 5)); -} - -int main(void) -{ - plan_tests(TEST_COUNT); - - test_contains_string(); - - return exit_status(); -} diff --git a/tests/unit/test_buffer_view.cpp b/tests/unit/test_buffer_view.cpp new file mode 100644 index 000000000..a129d4a9a --- /dev/null +++ b/tests/unit/test_buffer_view.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 EfficiOS, inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + + +#include +#include + +static const int TEST_COUNT = 5; + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +static void test_contains_string(void) +{ + const char buf[] = {'A', 'l', 'l', 'o', '\0'}; + struct lttng_buffer_view view = lttng_buffer_view_init(buf, 0, 5); + struct lttng_buffer_view view_minus_one = + lttng_buffer_view_init(buf, 0, 4); + + ok1(!lttng_buffer_view_contains_string(&view, buf, 4)); + ok1(lttng_buffer_view_contains_string(&view, buf, 5)); + ok1(!lttng_buffer_view_contains_string(&view, buf, 6)); + + ok1(!lttng_buffer_view_contains_string(&view_minus_one, buf, 4)); + ok1(!lttng_buffer_view_contains_string(&view_minus_one, buf, 5)); +} + +int main(void) +{ + plan_tests(TEST_COUNT); + + test_contains_string(); + + return exit_status(); +} diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c deleted file mode 100644 index 1ed14fa6c..000000000 --- a/tests/unit/test_condition.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * test_condition.c - * - * Unit tests for the condition API. - * - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 13 - -static -void test_condition_event_rule(void) -{ - int ret, i; - struct lttng_event_rule *tracepoint = NULL; - const struct lttng_event_rule *tracepoint_tmp = NULL; - enum lttng_event_rule_status status; - struct lttng_condition *condition = NULL; - struct lttng_condition *condition_from_buffer = NULL; - enum lttng_condition_status condition_status; - const char *pattern="my_event_*"; - const char *filter="msg_id == 23 && size >= 2048"; - const char *exclusions[] = { "my_event_test1", "my_event_test2", "my_event_test3" }; - struct lttng_log_level_rule *log_level_rule_at_least_as_severe = NULL; - struct lttng_payload buffer; - - lttng_payload_init(&buffer); - - /* Create log level rule. */ - log_level_rule_at_least_as_severe = - lttng_log_level_rule_at_least_as_severe_as_create( - LTTNG_LOGLEVEL_WARNING); - LTTNG_ASSERT(log_level_rule_at_least_as_severe); - - tracepoint = lttng_event_rule_user_tracepoint_create(); - ok(tracepoint, "user tracepoint"); - - status = lttng_event_rule_user_tracepoint_set_name_pattern(tracepoint, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting pattern"); - - status = lttng_event_rule_user_tracepoint_set_filter(tracepoint, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting filter"); - - status = lttng_event_rule_user_tracepoint_set_log_level_rule( - tracepoint, log_level_rule_at_least_as_severe); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting log level range"); - - for (i = 0; i < 3; i++) { - status = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion( - tracepoint, exclusions[i]); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting exclusion pattern"); - } - - condition = lttng_condition_event_rule_matches_create(tracepoint); - ok(condition, "Created condition"); - - condition_status = lttng_condition_event_rule_matches_get_rule( - condition, &tracepoint_tmp); - ok(condition_status == LTTNG_CONDITION_STATUS_OK, - "Getting event rule from event rule condition"); - ok(tracepoint == tracepoint_tmp, "lttng_condition_event_rule_get_rule provides a reference to the original rule"); - - ret = lttng_condition_serialize(condition, &buffer); - ok(ret == 0, "Condition serialized"); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload(&buffer, 0, -1); - - (void) lttng_condition_create_from_payload( - &view, &condition_from_buffer); - } - - ok(condition_from_buffer, "Condition created from payload is non-null"); - - ok(lttng_condition_is_equal(condition, condition_from_buffer), - "Serialized and de-serialized conditions are equal"); - - lttng_payload_reset(&buffer); - lttng_event_rule_destroy(tracepoint); - lttng_condition_destroy(condition); - lttng_condition_destroy(condition_from_buffer); - lttng_log_level_rule_destroy(log_level_rule_at_least_as_severe); -} - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_condition_event_rule(); - return exit_status(); -} diff --git a/tests/unit/test_condition.cpp b/tests/unit/test_condition.cpp new file mode 100644 index 000000000..1ed14fa6c --- /dev/null +++ b/tests/unit/test_condition.cpp @@ -0,0 +1,117 @@ +/* + * test_condition.c + * + * Unit tests for the condition API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 13 + +static +void test_condition_event_rule(void) +{ + int ret, i; + struct lttng_event_rule *tracepoint = NULL; + const struct lttng_event_rule *tracepoint_tmp = NULL; + enum lttng_event_rule_status status; + struct lttng_condition *condition = NULL; + struct lttng_condition *condition_from_buffer = NULL; + enum lttng_condition_status condition_status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *exclusions[] = { "my_event_test1", "my_event_test2", "my_event_test3" }; + struct lttng_log_level_rule *log_level_rule_at_least_as_severe = NULL; + struct lttng_payload buffer; + + lttng_payload_init(&buffer); + + /* Create log level rule. */ + log_level_rule_at_least_as_severe = + lttng_log_level_rule_at_least_as_severe_as_create( + LTTNG_LOGLEVEL_WARNING); + LTTNG_ASSERT(log_level_rule_at_least_as_severe); + + tracepoint = lttng_event_rule_user_tracepoint_create(); + ok(tracepoint, "user tracepoint"); + + status = lttng_event_rule_user_tracepoint_set_name_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting pattern"); + + status = lttng_event_rule_user_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting filter"); + + status = lttng_event_rule_user_tracepoint_set_log_level_rule( + tracepoint, log_level_rule_at_least_as_severe); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting log level range"); + + for (i = 0; i < 3; i++) { + status = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion( + tracepoint, exclusions[i]); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting exclusion pattern"); + } + + condition = lttng_condition_event_rule_matches_create(tracepoint); + ok(condition, "Created condition"); + + condition_status = lttng_condition_event_rule_matches_get_rule( + condition, &tracepoint_tmp); + ok(condition_status == LTTNG_CONDITION_STATUS_OK, + "Getting event rule from event rule condition"); + ok(tracepoint == tracepoint_tmp, "lttng_condition_event_rule_get_rule provides a reference to the original rule"); + + ret = lttng_condition_serialize(condition, &buffer); + ok(ret == 0, "Condition serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_condition_create_from_payload( + &view, &condition_from_buffer); + } + + ok(condition_from_buffer, "Condition created from payload is non-null"); + + ok(lttng_condition_is_equal(condition, condition_from_buffer), + "Serialized and de-serialized conditions are equal"); + + lttng_payload_reset(&buffer); + lttng_event_rule_destroy(tracepoint); + lttng_condition_destroy(condition); + lttng_condition_destroy(condition_from_buffer); + lttng_log_level_rule_destroy(log_level_rule_at_least_as_severe); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_condition_event_rule(); + return exit_status(); +} diff --git a/tests/unit/test_directory_handle.c b/tests/unit/test_directory_handle.c deleted file mode 100644 index ca1167165..000000000 --- a/tests/unit/test_directory_handle.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define TEST_COUNT 9 - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose = 3; -int lttng_opt_mi; - -#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG) - -/* - * Returns the number of tests that ran (irrespective of the result) or a - * negative value on error (will abort all tests). - */ -typedef int(test_func)(const char *test_base_path); - -static test_func test_rmdir_fail_non_empty; -static test_func test_rmdir_skip_non_empty; - -static test_func *const test_funcs[] = { - &test_rmdir_fail_non_empty, - &test_rmdir_skip_non_empty, -}; - -static bool dir_exists(const char *path) -{ - int ret; - struct stat st; - - ret = stat(path, &st); - return ret == 0 && S_ISDIR(st.st_mode); -} - -/* - * Create a non-empty folder hierarchy from a directory handle: - * - * test_root_name - * └── a - * └── b - * ├── c - * │   └── d - * └── e - * ├── f - * └── file1 - */ -static int create_non_empty_hierarchy_with_root( - struct lttng_directory_handle *test_dir_handle, - const char *test_root_name) -{ - int ret; - const int file_flags = O_WRONLY | O_CREAT | O_TRUNC; - const mode_t file_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; - char *branch_name = NULL; - - ret = asprintf(&branch_name, "%s/%s", test_root_name, "a/b/c/d"); - if (ret < 0) { - diag("Failed to format folder path"); - goto end; - } - ret = lttng_directory_handle_create_subdirectory_recursive( - test_dir_handle, - branch_name, - DIR_CREATION_MODE); - if (ret) { - diag("Failed to create test folder hierarchy %s", branch_name); - goto end; - } - - free(branch_name); - ret = asprintf(&branch_name, "%s/%s", test_root_name, "a/b/e/f"); - if (ret < 0) { - diag("Failed to format folder path"); - goto end; - } - ret = lttng_directory_handle_create_subdirectory_recursive( - test_dir_handle, - branch_name, - DIR_CREATION_MODE); - if (ret) { - diag("Failed to create test folder hierarchy %s", branch_name); - goto end; - } - - free(branch_name); - ret = asprintf(&branch_name, "%s/%s", test_root_name, "a/b/e/file1"); - if (ret < 0) { - diag("Failed to format file path"); - goto end; - } - ret = lttng_directory_handle_open_file( - test_dir_handle, branch_name, file_flags, file_mode); - if (ret < 0) { - diag("Failed to create file %s", branch_name); - goto end; - } - ret = close(ret); - if (ret) { - PERROR("Failed to close fd to newly created file %s", - branch_name); - goto end; - } -end: - free(branch_name); - return ret; -} - -/* Remove "file1" from the test folder hierarchy. */ -static -int remove_file_from_hierarchy(struct lttng_directory_handle *test_dir_handle, - const char *test_root_name) -{ - int ret; - char *file_name = NULL; - - ret = asprintf(&file_name, "%s/%s", test_root_name, "a/b/e/file1"); - if (ret < 0) { - diag("Failed to format file path"); - goto end; - } - - ret = lttng_directory_handle_unlink_file(test_dir_handle, - file_name); - if (ret) { - PERROR("Failed to unlink file %s", file_name); - goto end; - } -end: - free(file_name); - return ret; -} - -static int test_rmdir_fail_non_empty(const char *test_dir) -{ - int ret, tests_ran = 0; - struct lttng_directory_handle *test_dir_handle; - char *created_dir = NULL; - const char test_root_name[] = "fail_non_empty"; - char *test_dir_path = NULL; - - diag("rmdir (fail if non-empty)"); - - test_dir_handle = lttng_directory_handle_create(test_dir); - ok(test_dir_handle, "Initialized directory handle from the test directory"); - tests_ran++; - if (!test_dir_handle) { - ret = -1; - goto end; - } - - ret = create_non_empty_hierarchy_with_root(test_dir_handle, test_root_name); - if (ret) { - diag("Failed to setup folder/file hierarchy to run test"); - goto end; - } - - ret = lttng_directory_handle_remove_subdirectory_recursive( - test_dir_handle, test_root_name, - LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG); - ok(ret == -1, "Error returned when attempting to recursively remove non-empty hierarchy with LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG"); - tests_ran++; - - ret = remove_file_from_hierarchy(test_dir_handle, test_root_name); - if (ret) { - diag("Failed to remove file from test folder hierarchy"); - goto end; - } - - ret = lttng_directory_handle_remove_subdirectory_recursive( - test_dir_handle, test_root_name, - LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG); - ok(ret == 0, "No error returned when recursively removing empty hierarchy with LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG"); - tests_ran++; - - ret = asprintf(&test_dir_path, "%s/%s", test_dir, test_root_name); - if (ret < 0) { - diag("Failed to format test directory path"); - goto end; - } - ok(!dir_exists(test_dir_path) && errno == ENOENT, - "Folder hierarchy %s successfully removed", - test_dir_path); - tests_ran++; - ret = 0; -end: - lttng_directory_handle_put(test_dir_handle); - free(created_dir); - free(test_dir_path); - return ret == 0 ? tests_ran : ret; -} - -static int test_rmdir_skip_non_empty(const char *test_dir) -{ - int ret, tests_ran = 0; - struct lttng_directory_handle *test_dir_handle; - char *created_dir = NULL; - const char test_root_name[] = "skip_non_empty"; - char *test_dir_path = NULL; - - diag("rmdir (skip if non-empty)"); - - test_dir_handle = lttng_directory_handle_create(test_dir); - ok(test_dir_handle, "Initialized directory handle from the test directory"); - tests_ran++; - if (!test_dir_handle) { - ret = -1; - goto end; - } - - ret = create_non_empty_hierarchy_with_root(test_dir_handle, test_root_name); - if (ret) { - diag("Failed to setup folder/file hierarchy to run test"); - goto end; - } - - ret = lttng_directory_handle_remove_subdirectory_recursive( - test_dir_handle, test_root_name, - LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); - ok(ret == 0, "No error returned when attempting to recursively remove non-empty hierarchy with LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG"); - tests_ran++; - - ret = asprintf(&test_dir_path, "%s/%s", test_dir, test_root_name); - if (ret < 0) { - diag("Failed to format test directory path"); - goto end; - } - ok(dir_exists(test_dir_path), "Test directory still exists after skip"); - tests_ran++; - - ret = remove_file_from_hierarchy(test_dir_handle, test_root_name); - if (ret) { - diag("Failed to remove file from test folder hierarchy"); - goto end; - } - - ret = lttng_directory_handle_remove_subdirectory_recursive( - test_dir_handle, test_root_name, - LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); - ok(ret == 0, "No error returned when recursively removing empty hierarchy with LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG"); - tests_ran++; - - ok(!dir_exists(test_dir_path) && errno == ENOENT, - "Folder hierarchy %s successfully removed", - test_dir_path); - tests_ran++; - ret = 0; -end: - lttng_directory_handle_put(test_dir_handle); - free(created_dir); - free(test_dir_path); - return ret == 0 ? tests_ran : ret; -} - -int main(int argc, char **argv) -{ - int ret; - char test_dir[] = "/tmp/lttng-XXXXXX"; - int tests_left = TEST_COUNT; - size_t func_idx; - - plan_tests(TEST_COUNT); - - diag("lttng_directory_handle tests"); - - if (!mkdtemp(test_dir)) { - diag("Failed to generate temporary test directory"); - goto end; - } - - for (func_idx = 0; func_idx < sizeof(test_funcs) / sizeof(*test_funcs); - func_idx++) { - tests_left -= test_funcs[func_idx](test_dir); - } - if (tests_left) { - diag("Skipping %d tests that could not be executed due to a prior error", - tests_left); - skip(tests_left, "test due to an error"); - } -end: - ret = rmdir(test_dir); - if (ret) { - diag("Failed to clean-up test directory: %s", strerror(errno)); - } - return exit_status(); -} diff --git a/tests/unit/test_directory_handle.cpp b/tests/unit/test_directory_handle.cpp new file mode 100644 index 000000000..ca1167165 --- /dev/null +++ b/tests/unit/test_directory_handle.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TEST_COUNT 9 + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 3; +int lttng_opt_mi; + +#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG) + +/* + * Returns the number of tests that ran (irrespective of the result) or a + * negative value on error (will abort all tests). + */ +typedef int(test_func)(const char *test_base_path); + +static test_func test_rmdir_fail_non_empty; +static test_func test_rmdir_skip_non_empty; + +static test_func *const test_funcs[] = { + &test_rmdir_fail_non_empty, + &test_rmdir_skip_non_empty, +}; + +static bool dir_exists(const char *path) +{ + int ret; + struct stat st; + + ret = stat(path, &st); + return ret == 0 && S_ISDIR(st.st_mode); +} + +/* + * Create a non-empty folder hierarchy from a directory handle: + * + * test_root_name + * └── a + * └── b + * ├── c + * │   └── d + * └── e + * ├── f + * └── file1 + */ +static int create_non_empty_hierarchy_with_root( + struct lttng_directory_handle *test_dir_handle, + const char *test_root_name) +{ + int ret; + const int file_flags = O_WRONLY | O_CREAT | O_TRUNC; + const mode_t file_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + char *branch_name = NULL; + + ret = asprintf(&branch_name, "%s/%s", test_root_name, "a/b/c/d"); + if (ret < 0) { + diag("Failed to format folder path"); + goto end; + } + ret = lttng_directory_handle_create_subdirectory_recursive( + test_dir_handle, + branch_name, + DIR_CREATION_MODE); + if (ret) { + diag("Failed to create test folder hierarchy %s", branch_name); + goto end; + } + + free(branch_name); + ret = asprintf(&branch_name, "%s/%s", test_root_name, "a/b/e/f"); + if (ret < 0) { + diag("Failed to format folder path"); + goto end; + } + ret = lttng_directory_handle_create_subdirectory_recursive( + test_dir_handle, + branch_name, + DIR_CREATION_MODE); + if (ret) { + diag("Failed to create test folder hierarchy %s", branch_name); + goto end; + } + + free(branch_name); + ret = asprintf(&branch_name, "%s/%s", test_root_name, "a/b/e/file1"); + if (ret < 0) { + diag("Failed to format file path"); + goto end; + } + ret = lttng_directory_handle_open_file( + test_dir_handle, branch_name, file_flags, file_mode); + if (ret < 0) { + diag("Failed to create file %s", branch_name); + goto end; + } + ret = close(ret); + if (ret) { + PERROR("Failed to close fd to newly created file %s", + branch_name); + goto end; + } +end: + free(branch_name); + return ret; +} + +/* Remove "file1" from the test folder hierarchy. */ +static +int remove_file_from_hierarchy(struct lttng_directory_handle *test_dir_handle, + const char *test_root_name) +{ + int ret; + char *file_name = NULL; + + ret = asprintf(&file_name, "%s/%s", test_root_name, "a/b/e/file1"); + if (ret < 0) { + diag("Failed to format file path"); + goto end; + } + + ret = lttng_directory_handle_unlink_file(test_dir_handle, + file_name); + if (ret) { + PERROR("Failed to unlink file %s", file_name); + goto end; + } +end: + free(file_name); + return ret; +} + +static int test_rmdir_fail_non_empty(const char *test_dir) +{ + int ret, tests_ran = 0; + struct lttng_directory_handle *test_dir_handle; + char *created_dir = NULL; + const char test_root_name[] = "fail_non_empty"; + char *test_dir_path = NULL; + + diag("rmdir (fail if non-empty)"); + + test_dir_handle = lttng_directory_handle_create(test_dir); + ok(test_dir_handle, "Initialized directory handle from the test directory"); + tests_ran++; + if (!test_dir_handle) { + ret = -1; + goto end; + } + + ret = create_non_empty_hierarchy_with_root(test_dir_handle, test_root_name); + if (ret) { + diag("Failed to setup folder/file hierarchy to run test"); + goto end; + } + + ret = lttng_directory_handle_remove_subdirectory_recursive( + test_dir_handle, test_root_name, + LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG); + ok(ret == -1, "Error returned when attempting to recursively remove non-empty hierarchy with LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG"); + tests_ran++; + + ret = remove_file_from_hierarchy(test_dir_handle, test_root_name); + if (ret) { + diag("Failed to remove file from test folder hierarchy"); + goto end; + } + + ret = lttng_directory_handle_remove_subdirectory_recursive( + test_dir_handle, test_root_name, + LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG); + ok(ret == 0, "No error returned when recursively removing empty hierarchy with LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG"); + tests_ran++; + + ret = asprintf(&test_dir_path, "%s/%s", test_dir, test_root_name); + if (ret < 0) { + diag("Failed to format test directory path"); + goto end; + } + ok(!dir_exists(test_dir_path) && errno == ENOENT, + "Folder hierarchy %s successfully removed", + test_dir_path); + tests_ran++; + ret = 0; +end: + lttng_directory_handle_put(test_dir_handle); + free(created_dir); + free(test_dir_path); + return ret == 0 ? tests_ran : ret; +} + +static int test_rmdir_skip_non_empty(const char *test_dir) +{ + int ret, tests_ran = 0; + struct lttng_directory_handle *test_dir_handle; + char *created_dir = NULL; + const char test_root_name[] = "skip_non_empty"; + char *test_dir_path = NULL; + + diag("rmdir (skip if non-empty)"); + + test_dir_handle = lttng_directory_handle_create(test_dir); + ok(test_dir_handle, "Initialized directory handle from the test directory"); + tests_ran++; + if (!test_dir_handle) { + ret = -1; + goto end; + } + + ret = create_non_empty_hierarchy_with_root(test_dir_handle, test_root_name); + if (ret) { + diag("Failed to setup folder/file hierarchy to run test"); + goto end; + } + + ret = lttng_directory_handle_remove_subdirectory_recursive( + test_dir_handle, test_root_name, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); + ok(ret == 0, "No error returned when attempting to recursively remove non-empty hierarchy with LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG"); + tests_ran++; + + ret = asprintf(&test_dir_path, "%s/%s", test_dir, test_root_name); + if (ret < 0) { + diag("Failed to format test directory path"); + goto end; + } + ok(dir_exists(test_dir_path), "Test directory still exists after skip"); + tests_ran++; + + ret = remove_file_from_hierarchy(test_dir_handle, test_root_name); + if (ret) { + diag("Failed to remove file from test folder hierarchy"); + goto end; + } + + ret = lttng_directory_handle_remove_subdirectory_recursive( + test_dir_handle, test_root_name, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); + ok(ret == 0, "No error returned when recursively removing empty hierarchy with LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG"); + tests_ran++; + + ok(!dir_exists(test_dir_path) && errno == ENOENT, + "Folder hierarchy %s successfully removed", + test_dir_path); + tests_ran++; + ret = 0; +end: + lttng_directory_handle_put(test_dir_handle); + free(created_dir); + free(test_dir_path); + return ret == 0 ? tests_ran : ret; +} + +int main(int argc, char **argv) +{ + int ret; + char test_dir[] = "/tmp/lttng-XXXXXX"; + int tests_left = TEST_COUNT; + size_t func_idx; + + plan_tests(TEST_COUNT); + + diag("lttng_directory_handle tests"); + + if (!mkdtemp(test_dir)) { + diag("Failed to generate temporary test directory"); + goto end; + } + + for (func_idx = 0; func_idx < sizeof(test_funcs) / sizeof(*test_funcs); + func_idx++) { + tests_left -= test_funcs[func_idx](test_dir); + } + if (tests_left) { + diag("Skipping %d tests that could not be executed due to a prior error", + tests_left); + skip(tests_left, "test due to an error"); + } +end: + ret = rmdir(test_dir); + if (ret) { + diag("Failed to clean-up test directory: %s", strerror(errno)); + } + return exit_status(); +} diff --git a/tests/unit/test_event_expr_to_bytecode.c b/tests/unit/test_event_expr_to_bytecode.c deleted file mode 100644 index fae7eb26e..000000000 --- a/tests/unit/test_event_expr_to_bytecode.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2020 EfficiOS, Inc. - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include - -#define NR_TESTS 4 - -static -void test_event_payload_field(void) -{ - struct lttng_event_expr *event_expr; - struct lttng_bytecode *bytecode = NULL; - int ret; - - event_expr = lttng_event_expr_event_payload_field_create("tourlou"); - ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); - - ok(ret == 0, "event payload field"); - - lttng_event_expr_destroy(event_expr); - free(bytecode); -} - -static -void test_channel_context_field(void) -{ - struct lttng_event_expr *event_expr; - struct lttng_bytecode *bytecode = NULL; - int ret; - - event_expr = lttng_event_expr_channel_context_field_create("tourlou"); - ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); - - ok(ret == 0, "channel context field"); - - lttng_event_expr_destroy(event_expr); - free(bytecode); -} - -static -void test_app_specific_context_field(void) -{ - struct lttng_event_expr *event_expr; - struct lttng_bytecode *bytecode = NULL; - int ret; - - event_expr = lttng_event_expr_app_specific_context_field_create("Bob", "Leponge"); - ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); - - ok(ret == 0, "app-specific context field"); - - lttng_event_expr_destroy(event_expr); - free(bytecode); -} - -static -void test_array_field_element(void) -{ - struct lttng_event_expr *event_expr; - struct lttng_bytecode *bytecode = NULL; - int ret; - - event_expr = lttng_event_expr_event_payload_field_create("allo"); - event_expr = lttng_event_expr_array_field_element_create(event_expr, 168); - ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); - - ok(ret == 0, "array field element"); - - lttng_event_expr_destroy(event_expr); - free(bytecode); -} - -int main(void) -{ - plan_tests(NR_TESTS); - - test_event_payload_field(); - test_channel_context_field(); - test_app_specific_context_field(); - test_array_field_element(); - - return exit_status(); -} diff --git a/tests/unit/test_event_expr_to_bytecode.cpp b/tests/unit/test_event_expr_to_bytecode.cpp new file mode 100644 index 000000000..fae7eb26e --- /dev/null +++ b/tests/unit/test_event_expr_to_bytecode.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include + +#define NR_TESTS 4 + +static +void test_event_payload_field(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_event_payload_field_create("tourlou"); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "event payload field"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +static +void test_channel_context_field(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_channel_context_field_create("tourlou"); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "channel context field"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +static +void test_app_specific_context_field(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_app_specific_context_field_create("Bob", "Leponge"); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "app-specific context field"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +static +void test_array_field_element(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_event_payload_field_create("allo"); + event_expr = lttng_event_expr_array_field_element_create(event_expr, 168); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "array field element"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +int main(void) +{ + plan_tests(NR_TESTS); + + test_event_payload_field(); + test_channel_context_field(); + test_app_specific_context_field(); + test_array_field_element(); + + return exit_status(); +} diff --git a/tests/unit/test_event_rule.c b/tests/unit/test_event_rule.c deleted file mode 100644 index 0fc9ec770..000000000 --- a/tests/unit/test_event_rule.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Unit tests for the notification API. - * - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "bin/lttng/loglevel.h" - -/* For error.h. */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 212 - -struct tracepoint_test { - enum lttng_domain_type type; - bool support_name_pattern_exclusion; -}; - -typedef const char *(*log_level_name_getter)(int log_level); - -typedef struct lttng_event_rule *(*event_rule_create)(void); -typedef enum lttng_event_rule_status (*event_rule_set_log_level)( - struct lttng_event_rule *rule, - const struct lttng_log_level_rule *log_level_rule); - -static -void test_event_rule_kernel_tracepoint(void) -{ - struct lttng_event_rule *tracepoint = NULL; - struct lttng_event_rule *tracepoint_from_buffer = NULL; - enum lttng_event_rule_status status; - const char *pattern="my_event_*"; - const char *filter="msg_id == 23 && size >= 2048"; - const char *tmp; - struct lttng_payload payload; - - diag("Testing lttng_event_rule_kernel_tracepoint."); - - lttng_payload_init(&payload); - - tracepoint = lttng_event_rule_kernel_tracepoint_create(); - ok(tracepoint, "tracepoint object."); - - status = lttng_event_rule_kernel_tracepoint_set_name_pattern(tracepoint, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); - status = lttng_event_rule_kernel_tracepoint_get_name_pattern(tracepoint, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); - ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); - - status = lttng_event_rule_kernel_tracepoint_set_filter(tracepoint, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); - status = lttng_event_rule_kernel_tracepoint_get_filter(tracepoint, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); - ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - - ok(lttng_event_rule_serialize(tracepoint, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &tracepoint_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(tracepoint); - lttng_event_rule_destroy(tracepoint_from_buffer); -} - -static -void test_event_rule_user_tracepoint(void) -{ - int i; - unsigned int count; - struct lttng_event_rule *tracepoint = NULL; - struct lttng_event_rule *tracepoint_from_buffer = NULL; - enum lttng_event_rule_status status; - const char *pattern="my_event_*"; - const char *filter="msg_id == 23 && size >= 2048"; - const char *tmp; - const char *name_pattern_exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"}; - struct lttng_log_level_rule *log_level_rule = NULL; - const struct lttng_log_level_rule *log_level_rule_return = NULL; - struct lttng_payload payload; - - diag("Testing lttng_event_rule_user_tracepoint."); - - lttng_payload_init(&payload); - - log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); - LTTNG_ASSERT(log_level_rule); - - tracepoint = lttng_event_rule_user_tracepoint_create(); - ok(tracepoint, "user tracepoint object."); - - status = lttng_event_rule_user_tracepoint_set_name_pattern(tracepoint, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); - status = lttng_event_rule_user_tracepoint_get_name_pattern(tracepoint, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); - ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); - - status = lttng_event_rule_user_tracepoint_set_filter(tracepoint, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); - status = lttng_event_rule_user_tracepoint_get_filter(tracepoint, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); - ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - - status = lttng_event_rule_user_tracepoint_get_log_level_rule(tracepoint, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); - - status = lttng_event_rule_user_tracepoint_set_log_level_rule( - tracepoint, log_level_rule); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); - status = lttng_event_rule_user_tracepoint_get_log_level_rule( - tracepoint, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); - - /* Name pattern exclusions */ - for (i = 0; i < 3; i++) { - status = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion( - tracepoint, name_pattern_exclusions[i]); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "setting name pattern exclusions \"%s\"", - name_pattern_exclusions[i]); - } - - status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count( - tracepoint, &count); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "getting name pattern exclusion count."); - ok(count == 3, "count is %d/3", count); - - for (i = 0; i < count; i++) { - status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index( - tracepoint, i, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "getting name pattern exclusion at index %d.", - i); - ok(!strncmp(name_pattern_exclusions[i], tmp, - strlen(name_pattern_exclusions[i])), - "%s == %s.", tmp, name_pattern_exclusions[i]); - } - - ok(lttng_event_rule_serialize(tracepoint, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &tracepoint_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(tracepoint); - lttng_event_rule_destroy(tracepoint_from_buffer); - lttng_log_level_rule_destroy(log_level_rule); -} - -static void test_event_rule_syscall(void) -{ - struct lttng_event_rule *syscall = NULL; - struct lttng_event_rule *syscall_from_buffer = NULL; - enum lttng_event_rule_status status; - const char *pattern = "my_event_*"; - const char *filter = "msg_id == 23 && size >= 2048"; - const char *tmp; - struct lttng_payload payload; - - diag("Event rule syscall."); - - lttng_payload_init(&payload); - - syscall = lttng_event_rule_kernel_syscall_create(LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY); - ok(syscall, "syscall object."); - - status = lttng_event_rule_kernel_syscall_set_name_pattern(syscall, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); - status = lttng_event_rule_kernel_syscall_get_name_pattern(syscall, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); - ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); - - status = lttng_event_rule_kernel_syscall_set_filter(syscall, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); - status = lttng_event_rule_kernel_syscall_get_filter(syscall, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); - ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - - ok(lttng_event_rule_serialize(syscall, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &syscall_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(syscall, syscall_from_buffer), - "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(syscall); - lttng_event_rule_destroy(syscall_from_buffer); -} - -static -void test_event_rule_jul_logging(void) -{ - struct lttng_event_rule *jul_logging = NULL; - struct lttng_event_rule *jul_logging_from_buffer = NULL; - enum lttng_event_rule_status status; - const char *pattern="my_event_*"; - const char *filter="msg_id == 23 && size >= 2048"; - const char *tmp; - struct lttng_log_level_rule *log_level_rule = NULL; - const struct lttng_log_level_rule *log_level_rule_return = NULL; - struct lttng_payload payload; - - diag("Testing lttng_event_rule_user_jul_logging."); - - lttng_payload_init(&payload); - - log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); - LTTNG_ASSERT(log_level_rule); - - jul_logging = lttng_event_rule_jul_logging_create(); - ok(jul_logging, "jul_logging object."); - - status = lttng_event_rule_jul_logging_set_name_pattern(jul_logging, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); - status = lttng_event_rule_jul_logging_get_name_pattern(jul_logging, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); - ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); - - status = lttng_event_rule_jul_logging_set_filter(jul_logging, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); - status = lttng_event_rule_jul_logging_get_filter(jul_logging, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); - ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - - status = lttng_event_rule_jul_logging_get_log_level_rule(jul_logging, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); - - status = lttng_event_rule_jul_logging_set_log_level_rule( - jul_logging, log_level_rule); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); - status = lttng_event_rule_jul_logging_get_log_level_rule( - jul_logging, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); - - ok(lttng_event_rule_serialize(jul_logging, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &jul_logging_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(jul_logging, jul_logging_from_buffer), "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(jul_logging); - lttng_event_rule_destroy(jul_logging_from_buffer); - lttng_log_level_rule_destroy(log_level_rule); -} - -static -void test_event_rule_log4j_logging(void) -{ - struct lttng_event_rule *log4j_logging = NULL; - struct lttng_event_rule *log4j_logging_from_buffer = NULL; - enum lttng_event_rule_status status; - const char *pattern="my_event_*"; - const char *filter="msg_id == 23 && size >= 2048"; - const char *tmp; - struct lttng_log_level_rule *log_level_rule = NULL; - const struct lttng_log_level_rule *log_level_rule_return = NULL; - struct lttng_payload payload; - - diag("Testing lttng_event_rule_user_log4j_logging."); - - lttng_payload_init(&payload); - - log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); - LTTNG_ASSERT(log_level_rule); - - log4j_logging = lttng_event_rule_log4j_logging_create(); - ok(log4j_logging, "log4j_logging object."); - - status = lttng_event_rule_log4j_logging_set_name_pattern(log4j_logging, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); - status = lttng_event_rule_log4j_logging_get_name_pattern(log4j_logging, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); - ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); - - status = lttng_event_rule_log4j_logging_set_filter(log4j_logging, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); - status = lttng_event_rule_log4j_logging_get_filter(log4j_logging, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); - ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - - status = lttng_event_rule_log4j_logging_get_log_level_rule(log4j_logging, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); - - status = lttng_event_rule_log4j_logging_set_log_level_rule( - log4j_logging, log_level_rule); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); - status = lttng_event_rule_log4j_logging_get_log_level_rule( - log4j_logging, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); - - ok(lttng_event_rule_serialize(log4j_logging, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &log4j_logging_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(log4j_logging, log4j_logging_from_buffer), "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(log4j_logging); - lttng_event_rule_destroy(log4j_logging_from_buffer); - lttng_log_level_rule_destroy(log_level_rule); -} - -static -void test_event_rule_python_logging(void) -{ - struct lttng_event_rule *python_logging = NULL; - struct lttng_event_rule *python_logging_from_buffer = NULL; - enum lttng_event_rule_status status; - const char *pattern="my_event_*"; - const char *filter="msg_id == 23 && size >= 2048"; - const char *tmp; - struct lttng_log_level_rule *log_level_rule = NULL; - const struct lttng_log_level_rule *log_level_rule_return = NULL; - struct lttng_payload payload; - - diag("Testing lttng_event_rule_user_python_logging."); - - lttng_payload_init(&payload); - - log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); - LTTNG_ASSERT(log_level_rule); - - python_logging = lttng_event_rule_python_logging_create(); - ok(python_logging, "python_logging object."); - - status = lttng_event_rule_python_logging_set_name_pattern(python_logging, pattern); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); - status = lttng_event_rule_python_logging_get_name_pattern(python_logging, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); - ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); - - status = lttng_event_rule_python_logging_set_filter(python_logging, filter); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); - status = lttng_event_rule_python_logging_get_filter(python_logging, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); - ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - - status = lttng_event_rule_python_logging_get_log_level_rule(python_logging, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); - - status = lttng_event_rule_python_logging_set_log_level_rule( - python_logging, log_level_rule); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); - status = lttng_event_rule_python_logging_get_log_level_rule( - python_logging, &log_level_rule_return); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); - - ok(lttng_event_rule_serialize(python_logging, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &python_logging_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(python_logging, python_logging_from_buffer), "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(python_logging); - lttng_event_rule_destroy(python_logging_from_buffer); - lttng_log_level_rule_destroy(log_level_rule); -} - -static void test_event_rule_userspace_probe(void) -{ - struct lttng_event_rule *uprobe = NULL; - struct lttng_event_rule *uprobe_from_buffer = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = - NULL; - struct lttng_userspace_probe_location *probe_location = NULL; - const struct lttng_userspace_probe_location *probe_location_tmp = NULL; - enum lttng_event_rule_status status; - - const char *probe_name = "my_probe."; - const char *tmp; - struct lttng_payload payload; - - diag("Event rule uprobe."); - - lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); - if (!lookup_method) { - fail("Setup error on userspace probe lookup method creation."); - goto end; - } - - probe_location = lttng_userspace_probe_location_function_create( - "/proc/self/exe", - "lttng_userspace_probe_location_tracepoint_create", - lookup_method); - if (!probe_location) { - fail("Setup error on userspace probe location creation."); - goto end; - } - - /* Ownership transferred to the probe location function object. */ - lookup_method = NULL; - - lttng_payload_init(&payload); - - uprobe = lttng_event_rule_kernel_uprobe_create(probe_location); - ok(uprobe, "uprobe event rule object creation."); - - status = lttng_event_rule_kernel_uprobe_get_location( - uprobe, &probe_location_tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Getting uprobe event rule location."); - ok(lttng_userspace_probe_location_is_equal( - probe_location, probe_location_tmp), - "Location is equal."); - - status = lttng_event_rule_kernel_uprobe_set_event_name(uprobe, probe_name); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting uprobe event rule name: %s.", probe_name); - status = lttng_event_rule_kernel_uprobe_get_event_name(uprobe, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe name."); - ok(!strcmp(probe_name, tmp), "Uprobe name are equal."); - - ok(lttng_event_rule_serialize(uprobe, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &uprobe_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(uprobe, uprobe_from_buffer), - "serialized and from buffer are equal."); - -end: - lttng_payload_reset(&payload); - lttng_event_rule_destroy(uprobe); - lttng_event_rule_destroy(uprobe_from_buffer); - lttng_userspace_probe_location_destroy(probe_location); - lttng_userspace_probe_location_lookup_method_destroy(lookup_method); -} - -static void test_event_rule_kernel_probe_by_location( - const struct lttng_kernel_probe_location *location) -{ - struct lttng_event_rule *kprobe = NULL; - struct lttng_event_rule *kprobe_from_buffer = NULL; - enum lttng_event_rule_status status; - const struct lttng_kernel_probe_location *_location; - - const char *probe_name = "my_probe"; - const char *tmp; - struct lttng_payload payload; - - diag("Event rule kprobe for location type %d.", - lttng_kernel_probe_location_get_type(location)); - - lttng_payload_init(&payload); - - kprobe = lttng_event_rule_kernel_kprobe_create(location); - ok(kprobe, "kprobe event rule object creation."); - - status = lttng_event_rule_kernel_kprobe_get_location(kprobe, &_location); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Getting kprobe event rule location."); - ok(lttng_kernel_probe_location_is_equal(location, _location), "Locations are equal."); - - status = lttng_event_rule_kernel_kprobe_set_event_name(kprobe, probe_name); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting kprobe event rule name: %s.", probe_name); - status = lttng_event_rule_kernel_kprobe_get_event_name(kprobe, &tmp); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe name."); - ok(!strcmp(probe_name, tmp), "kprobe name are equal."); - - ok(lttng_event_rule_serialize(kprobe, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_event_rule_create_from_payload( - &view, &kprobe_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_event_rule_is_equal(kprobe, kprobe_from_buffer), - "serialized and from buffer are equal."); - - lttng_payload_reset(&payload); - lttng_event_rule_destroy(kprobe); - lttng_event_rule_destroy(kprobe_from_buffer); -} - -static void test_event_rule_kernel_probe(void) -{ - struct lttng_kernel_probe_location *address_location = NULL; - struct lttng_kernel_probe_location *symbol_location = NULL; - - address_location = lttng_kernel_probe_location_address_create(50); - symbol_location = lttng_kernel_probe_location_symbol_create("une_bonne", 50); - LTTNG_ASSERT(address_location); - LTTNG_ASSERT(symbol_location); - - test_event_rule_kernel_probe_by_location(address_location); - test_event_rule_kernel_probe_by_location(symbol_location); - - lttng_kernel_probe_location_destroy(address_location); - lttng_kernel_probe_location_destroy(symbol_location); -} - -static void test_set_event_rule_log_level_rules( - struct lttng_event_rule *event_rule, - event_rule_set_log_level set_log_level, - int log_level, - enum lttng_event_rule_status *exactly_status, - enum lttng_event_rule_status *as_severe_status) -{ - struct lttng_log_level_rule *log_level_rule; - - log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create( - log_level); - LTTNG_ASSERT(log_level_rule); - - *as_severe_status = set_log_level( - event_rule, log_level_rule); - lttng_log_level_rule_destroy(log_level_rule); - - log_level_rule = lttng_log_level_rule_exactly_create(log_level); - LTTNG_ASSERT(log_level_rule); - - *exactly_status = set_log_level( - event_rule, log_level_rule); - lttng_log_level_rule_destroy(log_level_rule); -} - -static void test_event_rule_log_level_generic(enum lttng_event_rule_type event_rule_type, - log_level_name_getter get_log_level_name, - event_rule_create create_event_rule, - event_rule_set_log_level set_log_level, - const int tagged_log_level_values[], - size_t tagged_log_level_values_count, - const int valid_log_level_values[], - size_t valid_log_level_values_count, - const int invalid_log_level_values[], - size_t invalid_log_level_values_count) -{ - size_t i; - struct lttng_event_rule *rule; - enum lttng_event_rule_status er_exactly_status, er_as_severe_status; - const char *event_rule_type_str = lttng_event_rule_type_str(event_rule_type); - - - diag("Test %s event rule + log level rule", event_rule_type_str); - - rule = create_event_rule(); - LTTNG_ASSERT(rule); - - for (i = 0; i < tagged_log_level_values_count; i++) { - const int tagged_log_level_value = tagged_log_level_values[i]; - - test_set_event_rule_log_level_rules(rule, set_log_level, - tagged_log_level_value, - &er_exactly_status, &er_as_severe_status); - ok(er_exactly_status == LTTNG_EVENT_RULE_STATUS_OK, - "Log level rule \"exactly\" accepted by %s event rule: level = %s", - event_rule_type_str, - get_log_level_name( - tagged_log_level_value)); - ok(er_as_severe_status == LTTNG_EVENT_RULE_STATUS_OK, - "Log level rule \"as least as severe as\" accepted by %s event rule: level = %s", - event_rule_type_str, - get_log_level_name( - tagged_log_level_value)); - } - - for (i = 0; i < valid_log_level_values_count; i++) { - const int valid_log_level_value = valid_log_level_values[i]; - - test_set_event_rule_log_level_rules(rule, set_log_level, - valid_log_level_value, - &er_exactly_status, &er_as_severe_status); - ok(er_exactly_status == LTTNG_EVENT_RULE_STATUS_OK, - "Log level rule \"exactly\" accepted by %s event rule: level = %d", - event_rule_type_str, - valid_log_level_value); - ok(er_as_severe_status == LTTNG_EVENT_RULE_STATUS_OK, - "Log level rule \"as least as severe as\" accepted by %s event rule: level = %d", - event_rule_type_str, - valid_log_level_value); - } - - for (i = 0; i < invalid_log_level_values_count; i++) { - const int invalid_log_level_value = invalid_log_level_values[i]; - - test_set_event_rule_log_level_rules(rule, set_log_level, - invalid_log_level_value, - &er_exactly_status, &er_as_severe_status); - ok(er_exactly_status == LTTNG_EVENT_RULE_STATUS_INVALID, - "Log level rule \"exactly\" rejected by %s event rule: level = %d", - event_rule_type_str, - invalid_log_level_value); - ok(er_as_severe_status == LTTNG_EVENT_RULE_STATUS_INVALID, - "Log level rule \"as least as severe as\" rejected by %s event rule: level = %d", - event_rule_type_str, - invalid_log_level_value); - } - - lttng_event_rule_destroy(rule); -} - -static void test_event_rule_log_level_ust(void) -{ - const int tagged_log_level_values[] = { - LTTNG_LOGLEVEL_EMERG, - LTTNG_LOGLEVEL_ALERT, - LTTNG_LOGLEVEL_CRIT, - LTTNG_LOGLEVEL_ERR, - LTTNG_LOGLEVEL_WARNING, - LTTNG_LOGLEVEL_NOTICE, - LTTNG_LOGLEVEL_INFO, - LTTNG_LOGLEVEL_DEBUG_SYSTEM, - LTTNG_LOGLEVEL_DEBUG_PROGRAM, - LTTNG_LOGLEVEL_DEBUG_PROCESS, - LTTNG_LOGLEVEL_DEBUG_MODULE, - LTTNG_LOGLEVEL_DEBUG_UNIT, - LTTNG_LOGLEVEL_DEBUG_FUNCTION, - LTTNG_LOGLEVEL_DEBUG_LINE, - LTTNG_LOGLEVEL_DEBUG, - }; - const int invalid_log_level_values[] = { - -1980, - 1995, - LTTNG_LOGLEVEL_DEBUG + 1, - LTTNG_LOGLEVEL_EMERG - 1, - }; - - test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT, - loglevel_value_to_name, - lttng_event_rule_user_tracepoint_create, - lttng_event_rule_user_tracepoint_set_log_level_rule, - tagged_log_level_values, - ARRAY_SIZE(tagged_log_level_values), NULL, 0, - invalid_log_level_values, - ARRAY_SIZE(invalid_log_level_values)); -} - -static void test_event_rule_log_level_jul(void) -{ - const int tagged_log_level_values[] = { - LTTNG_LOGLEVEL_JUL_OFF, - LTTNG_LOGLEVEL_JUL_SEVERE, - LTTNG_LOGLEVEL_JUL_WARNING, - LTTNG_LOGLEVEL_JUL_INFO, - LTTNG_LOGLEVEL_JUL_CONFIG, - LTTNG_LOGLEVEL_JUL_FINE, - LTTNG_LOGLEVEL_JUL_FINER, - LTTNG_LOGLEVEL_JUL_FINEST, - LTTNG_LOGLEVEL_JUL_ALL, - }; - const int valid_log_level_values[] = { - 0, - -1980, - 1995 - }; - - test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_JUL_LOGGING, - loglevel_jul_value_to_name, - lttng_event_rule_jul_logging_create, - lttng_event_rule_jul_logging_set_log_level_rule, - tagged_log_level_values, - ARRAY_SIZE(tagged_log_level_values), - valid_log_level_values, - ARRAY_SIZE(valid_log_level_values), NULL, 0); -} - -static void test_event_rule_log_level_log4j(void) -{ - const int tagged_log_level_values[] = { - LTTNG_LOGLEVEL_LOG4J_OFF, - LTTNG_LOGLEVEL_LOG4J_FATAL, - LTTNG_LOGLEVEL_LOG4J_ERROR, - LTTNG_LOGLEVEL_LOG4J_WARN, - LTTNG_LOGLEVEL_LOG4J_INFO, - LTTNG_LOGLEVEL_LOG4J_DEBUG, - LTTNG_LOGLEVEL_LOG4J_TRACE, - LTTNG_LOGLEVEL_LOG4J_ALL, - }; - const int valid_log_level_values[] = { - 0 - -1980, - 1995 - }; - - test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING, - loglevel_log4j_value_to_name, - lttng_event_rule_log4j_logging_create, - lttng_event_rule_log4j_logging_set_log_level_rule, - tagged_log_level_values, - ARRAY_SIZE(tagged_log_level_values), - valid_log_level_values, - ARRAY_SIZE(valid_log_level_values), NULL, 0); -} - -static void test_event_rule_log_level_python(void) -{ - const int tagged_log_level_values[] = { - LTTNG_LOGLEVEL_PYTHON_CRITICAL, - LTTNG_LOGLEVEL_PYTHON_ERROR, - LTTNG_LOGLEVEL_PYTHON_WARNING, - LTTNG_LOGLEVEL_PYTHON_INFO, - LTTNG_LOGLEVEL_PYTHON_DEBUG, - LTTNG_LOGLEVEL_PYTHON_NOTSET, - }; - const int valid_log_level_values[] = { - 45, - 35, - 0, - -657, - }; - - test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING, - loglevel_python_value_to_name, - lttng_event_rule_python_logging_create, - lttng_event_rule_python_logging_set_log_level_rule, - tagged_log_level_values, - ARRAY_SIZE(tagged_log_level_values), - valid_log_level_values, - ARRAY_SIZE(valid_log_level_values), - NULL, 0); -} - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_event_rule_kernel_tracepoint(); - test_event_rule_user_tracepoint(); - test_event_rule_syscall(); - test_event_rule_userspace_probe(); - test_event_rule_kernel_probe(); - test_event_rule_log4j_logging(); - test_event_rule_jul_logging(); - test_event_rule_python_logging(); - test_event_rule_log_level_ust(); - test_event_rule_log_level_jul(); - test_event_rule_log_level_log4j(); - test_event_rule_log_level_python(); - return exit_status(); -} diff --git a/tests/unit/test_event_rule.cpp b/tests/unit/test_event_rule.cpp new file mode 100644 index 000000000..0fc9ec770 --- /dev/null +++ b/tests/unit/test_event_rule.cpp @@ -0,0 +1,830 @@ +/* + * Unit tests for the notification API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bin/lttng/loglevel.h" + +/* For error.h. */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 212 + +struct tracepoint_test { + enum lttng_domain_type type; + bool support_name_pattern_exclusion; +}; + +typedef const char *(*log_level_name_getter)(int log_level); + +typedef struct lttng_event_rule *(*event_rule_create)(void); +typedef enum lttng_event_rule_status (*event_rule_set_log_level)( + struct lttng_event_rule *rule, + const struct lttng_log_level_rule *log_level_rule); + +static +void test_event_rule_kernel_tracepoint(void) +{ + struct lttng_event_rule *tracepoint = NULL; + struct lttng_event_rule *tracepoint_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *tmp; + struct lttng_payload payload; + + diag("Testing lttng_event_rule_kernel_tracepoint."); + + lttng_payload_init(&payload); + + tracepoint = lttng_event_rule_kernel_tracepoint_create(); + ok(tracepoint, "tracepoint object."); + + status = lttng_event_rule_kernel_tracepoint_set_name_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_kernel_tracepoint_get_name_pattern(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_kernel_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_kernel_tracepoint_get_filter(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + ok(lttng_event_rule_serialize(tracepoint, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &tracepoint_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(tracepoint); + lttng_event_rule_destroy(tracepoint_from_buffer); +} + +static +void test_event_rule_user_tracepoint(void) +{ + int i; + unsigned int count; + struct lttng_event_rule *tracepoint = NULL; + struct lttng_event_rule *tracepoint_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *tmp; + const char *name_pattern_exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"}; + struct lttng_log_level_rule *log_level_rule = NULL; + const struct lttng_log_level_rule *log_level_rule_return = NULL; + struct lttng_payload payload; + + diag("Testing lttng_event_rule_user_tracepoint."); + + lttng_payload_init(&payload); + + log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); + LTTNG_ASSERT(log_level_rule); + + tracepoint = lttng_event_rule_user_tracepoint_create(); + ok(tracepoint, "user tracepoint object."); + + status = lttng_event_rule_user_tracepoint_set_name_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_user_tracepoint_get_name_pattern(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_user_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_user_tracepoint_get_filter(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + status = lttng_event_rule_user_tracepoint_get_log_level_rule(tracepoint, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); + + status = lttng_event_rule_user_tracepoint_set_log_level_rule( + tracepoint, log_level_rule); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); + status = lttng_event_rule_user_tracepoint_get_log_level_rule( + tracepoint, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); + + /* Name pattern exclusions */ + for (i = 0; i < 3; i++) { + status = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion( + tracepoint, name_pattern_exclusions[i]); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "setting name pattern exclusions \"%s\"", + name_pattern_exclusions[i]); + } + + status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count( + tracepoint, &count); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "getting name pattern exclusion count."); + ok(count == 3, "count is %d/3", count); + + for (i = 0; i < count; i++) { + status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index( + tracepoint, i, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "getting name pattern exclusion at index %d.", + i); + ok(!strncmp(name_pattern_exclusions[i], tmp, + strlen(name_pattern_exclusions[i])), + "%s == %s.", tmp, name_pattern_exclusions[i]); + } + + ok(lttng_event_rule_serialize(tracepoint, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &tracepoint_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(tracepoint); + lttng_event_rule_destroy(tracepoint_from_buffer); + lttng_log_level_rule_destroy(log_level_rule); +} + +static void test_event_rule_syscall(void) +{ + struct lttng_event_rule *syscall = NULL; + struct lttng_event_rule *syscall_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern = "my_event_*"; + const char *filter = "msg_id == 23 && size >= 2048"; + const char *tmp; + struct lttng_payload payload; + + diag("Event rule syscall."); + + lttng_payload_init(&payload); + + syscall = lttng_event_rule_kernel_syscall_create(LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY); + ok(syscall, "syscall object."); + + status = lttng_event_rule_kernel_syscall_set_name_pattern(syscall, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_kernel_syscall_get_name_pattern(syscall, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_kernel_syscall_set_filter(syscall, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_kernel_syscall_get_filter(syscall, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + ok(lttng_event_rule_serialize(syscall, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &syscall_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(syscall, syscall_from_buffer), + "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(syscall); + lttng_event_rule_destroy(syscall_from_buffer); +} + +static +void test_event_rule_jul_logging(void) +{ + struct lttng_event_rule *jul_logging = NULL; + struct lttng_event_rule *jul_logging_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *tmp; + struct lttng_log_level_rule *log_level_rule = NULL; + const struct lttng_log_level_rule *log_level_rule_return = NULL; + struct lttng_payload payload; + + diag("Testing lttng_event_rule_user_jul_logging."); + + lttng_payload_init(&payload); + + log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); + LTTNG_ASSERT(log_level_rule); + + jul_logging = lttng_event_rule_jul_logging_create(); + ok(jul_logging, "jul_logging object."); + + status = lttng_event_rule_jul_logging_set_name_pattern(jul_logging, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_jul_logging_get_name_pattern(jul_logging, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_jul_logging_set_filter(jul_logging, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_jul_logging_get_filter(jul_logging, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + status = lttng_event_rule_jul_logging_get_log_level_rule(jul_logging, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); + + status = lttng_event_rule_jul_logging_set_log_level_rule( + jul_logging, log_level_rule); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); + status = lttng_event_rule_jul_logging_get_log_level_rule( + jul_logging, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); + + ok(lttng_event_rule_serialize(jul_logging, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &jul_logging_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(jul_logging, jul_logging_from_buffer), "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(jul_logging); + lttng_event_rule_destroy(jul_logging_from_buffer); + lttng_log_level_rule_destroy(log_level_rule); +} + +static +void test_event_rule_log4j_logging(void) +{ + struct lttng_event_rule *log4j_logging = NULL; + struct lttng_event_rule *log4j_logging_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *tmp; + struct lttng_log_level_rule *log_level_rule = NULL; + const struct lttng_log_level_rule *log_level_rule_return = NULL; + struct lttng_payload payload; + + diag("Testing lttng_event_rule_user_log4j_logging."); + + lttng_payload_init(&payload); + + log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); + LTTNG_ASSERT(log_level_rule); + + log4j_logging = lttng_event_rule_log4j_logging_create(); + ok(log4j_logging, "log4j_logging object."); + + status = lttng_event_rule_log4j_logging_set_name_pattern(log4j_logging, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_log4j_logging_get_name_pattern(log4j_logging, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_log4j_logging_set_filter(log4j_logging, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_log4j_logging_get_filter(log4j_logging, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + status = lttng_event_rule_log4j_logging_get_log_level_rule(log4j_logging, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); + + status = lttng_event_rule_log4j_logging_set_log_level_rule( + log4j_logging, log_level_rule); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); + status = lttng_event_rule_log4j_logging_get_log_level_rule( + log4j_logging, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); + + ok(lttng_event_rule_serialize(log4j_logging, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &log4j_logging_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(log4j_logging, log4j_logging_from_buffer), "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(log4j_logging); + lttng_event_rule_destroy(log4j_logging_from_buffer); + lttng_log_level_rule_destroy(log_level_rule); +} + +static +void test_event_rule_python_logging(void) +{ + struct lttng_event_rule *python_logging = NULL; + struct lttng_event_rule *python_logging_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *tmp; + struct lttng_log_level_rule *log_level_rule = NULL; + const struct lttng_log_level_rule *log_level_rule_return = NULL; + struct lttng_payload payload; + + diag("Testing lttng_event_rule_user_python_logging."); + + lttng_payload_init(&payload); + + log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); + LTTNG_ASSERT(log_level_rule); + + python_logging = lttng_event_rule_python_logging_create(); + ok(python_logging, "python_logging object."); + + status = lttng_event_rule_python_logging_set_name_pattern(python_logging, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_python_logging_get_name_pattern(python_logging, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_python_logging_set_filter(python_logging, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_python_logging_get_filter(python_logging, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + status = lttng_event_rule_python_logging_get_log_level_rule(python_logging, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); + + status = lttng_event_rule_python_logging_set_log_level_rule( + python_logging, log_level_rule); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); + status = lttng_event_rule_python_logging_get_log_level_rule( + python_logging, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); + + ok(lttng_event_rule_serialize(python_logging, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &python_logging_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(python_logging, python_logging_from_buffer), "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(python_logging); + lttng_event_rule_destroy(python_logging_from_buffer); + lttng_log_level_rule_destroy(log_level_rule); +} + +static void test_event_rule_userspace_probe(void) +{ + struct lttng_event_rule *uprobe = NULL; + struct lttng_event_rule *uprobe_from_buffer = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = + NULL; + struct lttng_userspace_probe_location *probe_location = NULL; + const struct lttng_userspace_probe_location *probe_location_tmp = NULL; + enum lttng_event_rule_status status; + + const char *probe_name = "my_probe."; + const char *tmp; + struct lttng_payload payload; + + diag("Event rule uprobe."); + + lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + fail("Setup error on userspace probe lookup method creation."); + goto end; + } + + probe_location = lttng_userspace_probe_location_function_create( + "/proc/self/exe", + "lttng_userspace_probe_location_tracepoint_create", + lookup_method); + if (!probe_location) { + fail("Setup error on userspace probe location creation."); + goto end; + } + + /* Ownership transferred to the probe location function object. */ + lookup_method = NULL; + + lttng_payload_init(&payload); + + uprobe = lttng_event_rule_kernel_uprobe_create(probe_location); + ok(uprobe, "uprobe event rule object creation."); + + status = lttng_event_rule_kernel_uprobe_get_location( + uprobe, &probe_location_tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Getting uprobe event rule location."); + ok(lttng_userspace_probe_location_is_equal( + probe_location, probe_location_tmp), + "Location is equal."); + + status = lttng_event_rule_kernel_uprobe_set_event_name(uprobe, probe_name); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting uprobe event rule name: %s.", probe_name); + status = lttng_event_rule_kernel_uprobe_get_event_name(uprobe, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe name."); + ok(!strcmp(probe_name, tmp), "Uprobe name are equal."); + + ok(lttng_event_rule_serialize(uprobe, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &uprobe_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(uprobe, uprobe_from_buffer), + "serialized and from buffer are equal."); + +end: + lttng_payload_reset(&payload); + lttng_event_rule_destroy(uprobe); + lttng_event_rule_destroy(uprobe_from_buffer); + lttng_userspace_probe_location_destroy(probe_location); + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); +} + +static void test_event_rule_kernel_probe_by_location( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_event_rule *kprobe = NULL; + struct lttng_event_rule *kprobe_from_buffer = NULL; + enum lttng_event_rule_status status; + const struct lttng_kernel_probe_location *_location; + + const char *probe_name = "my_probe"; + const char *tmp; + struct lttng_payload payload; + + diag("Event rule kprobe for location type %d.", + lttng_kernel_probe_location_get_type(location)); + + lttng_payload_init(&payload); + + kprobe = lttng_event_rule_kernel_kprobe_create(location); + ok(kprobe, "kprobe event rule object creation."); + + status = lttng_event_rule_kernel_kprobe_get_location(kprobe, &_location); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Getting kprobe event rule location."); + ok(lttng_kernel_probe_location_is_equal(location, _location), "Locations are equal."); + + status = lttng_event_rule_kernel_kprobe_set_event_name(kprobe, probe_name); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule name: %s.", probe_name); + status = lttng_event_rule_kernel_kprobe_get_event_name(kprobe, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe name."); + ok(!strcmp(probe_name, tmp), "kprobe name are equal."); + + ok(lttng_event_rule_serialize(kprobe, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &kprobe_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(kprobe, kprobe_from_buffer), + "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(kprobe); + lttng_event_rule_destroy(kprobe_from_buffer); +} + +static void test_event_rule_kernel_probe(void) +{ + struct lttng_kernel_probe_location *address_location = NULL; + struct lttng_kernel_probe_location *symbol_location = NULL; + + address_location = lttng_kernel_probe_location_address_create(50); + symbol_location = lttng_kernel_probe_location_symbol_create("une_bonne", 50); + LTTNG_ASSERT(address_location); + LTTNG_ASSERT(symbol_location); + + test_event_rule_kernel_probe_by_location(address_location); + test_event_rule_kernel_probe_by_location(symbol_location); + + lttng_kernel_probe_location_destroy(address_location); + lttng_kernel_probe_location_destroy(symbol_location); +} + +static void test_set_event_rule_log_level_rules( + struct lttng_event_rule *event_rule, + event_rule_set_log_level set_log_level, + int log_level, + enum lttng_event_rule_status *exactly_status, + enum lttng_event_rule_status *as_severe_status) +{ + struct lttng_log_level_rule *log_level_rule; + + log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create( + log_level); + LTTNG_ASSERT(log_level_rule); + + *as_severe_status = set_log_level( + event_rule, log_level_rule); + lttng_log_level_rule_destroy(log_level_rule); + + log_level_rule = lttng_log_level_rule_exactly_create(log_level); + LTTNG_ASSERT(log_level_rule); + + *exactly_status = set_log_level( + event_rule, log_level_rule); + lttng_log_level_rule_destroy(log_level_rule); +} + +static void test_event_rule_log_level_generic(enum lttng_event_rule_type event_rule_type, + log_level_name_getter get_log_level_name, + event_rule_create create_event_rule, + event_rule_set_log_level set_log_level, + const int tagged_log_level_values[], + size_t tagged_log_level_values_count, + const int valid_log_level_values[], + size_t valid_log_level_values_count, + const int invalid_log_level_values[], + size_t invalid_log_level_values_count) +{ + size_t i; + struct lttng_event_rule *rule; + enum lttng_event_rule_status er_exactly_status, er_as_severe_status; + const char *event_rule_type_str = lttng_event_rule_type_str(event_rule_type); + + + diag("Test %s event rule + log level rule", event_rule_type_str); + + rule = create_event_rule(); + LTTNG_ASSERT(rule); + + for (i = 0; i < tagged_log_level_values_count; i++) { + const int tagged_log_level_value = tagged_log_level_values[i]; + + test_set_event_rule_log_level_rules(rule, set_log_level, + tagged_log_level_value, + &er_exactly_status, &er_as_severe_status); + ok(er_exactly_status == LTTNG_EVENT_RULE_STATUS_OK, + "Log level rule \"exactly\" accepted by %s event rule: level = %s", + event_rule_type_str, + get_log_level_name( + tagged_log_level_value)); + ok(er_as_severe_status == LTTNG_EVENT_RULE_STATUS_OK, + "Log level rule \"as least as severe as\" accepted by %s event rule: level = %s", + event_rule_type_str, + get_log_level_name( + tagged_log_level_value)); + } + + for (i = 0; i < valid_log_level_values_count; i++) { + const int valid_log_level_value = valid_log_level_values[i]; + + test_set_event_rule_log_level_rules(rule, set_log_level, + valid_log_level_value, + &er_exactly_status, &er_as_severe_status); + ok(er_exactly_status == LTTNG_EVENT_RULE_STATUS_OK, + "Log level rule \"exactly\" accepted by %s event rule: level = %d", + event_rule_type_str, + valid_log_level_value); + ok(er_as_severe_status == LTTNG_EVENT_RULE_STATUS_OK, + "Log level rule \"as least as severe as\" accepted by %s event rule: level = %d", + event_rule_type_str, + valid_log_level_value); + } + + for (i = 0; i < invalid_log_level_values_count; i++) { + const int invalid_log_level_value = invalid_log_level_values[i]; + + test_set_event_rule_log_level_rules(rule, set_log_level, + invalid_log_level_value, + &er_exactly_status, &er_as_severe_status); + ok(er_exactly_status == LTTNG_EVENT_RULE_STATUS_INVALID, + "Log level rule \"exactly\" rejected by %s event rule: level = %d", + event_rule_type_str, + invalid_log_level_value); + ok(er_as_severe_status == LTTNG_EVENT_RULE_STATUS_INVALID, + "Log level rule \"as least as severe as\" rejected by %s event rule: level = %d", + event_rule_type_str, + invalid_log_level_value); + } + + lttng_event_rule_destroy(rule); +} + +static void test_event_rule_log_level_ust(void) +{ + const int tagged_log_level_values[] = { + LTTNG_LOGLEVEL_EMERG, + LTTNG_LOGLEVEL_ALERT, + LTTNG_LOGLEVEL_CRIT, + LTTNG_LOGLEVEL_ERR, + LTTNG_LOGLEVEL_WARNING, + LTTNG_LOGLEVEL_NOTICE, + LTTNG_LOGLEVEL_INFO, + LTTNG_LOGLEVEL_DEBUG_SYSTEM, + LTTNG_LOGLEVEL_DEBUG_PROGRAM, + LTTNG_LOGLEVEL_DEBUG_PROCESS, + LTTNG_LOGLEVEL_DEBUG_MODULE, + LTTNG_LOGLEVEL_DEBUG_UNIT, + LTTNG_LOGLEVEL_DEBUG_FUNCTION, + LTTNG_LOGLEVEL_DEBUG_LINE, + LTTNG_LOGLEVEL_DEBUG, + }; + const int invalid_log_level_values[] = { + -1980, + 1995, + LTTNG_LOGLEVEL_DEBUG + 1, + LTTNG_LOGLEVEL_EMERG - 1, + }; + + test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT, + loglevel_value_to_name, + lttng_event_rule_user_tracepoint_create, + lttng_event_rule_user_tracepoint_set_log_level_rule, + tagged_log_level_values, + ARRAY_SIZE(tagged_log_level_values), NULL, 0, + invalid_log_level_values, + ARRAY_SIZE(invalid_log_level_values)); +} + +static void test_event_rule_log_level_jul(void) +{ + const int tagged_log_level_values[] = { + LTTNG_LOGLEVEL_JUL_OFF, + LTTNG_LOGLEVEL_JUL_SEVERE, + LTTNG_LOGLEVEL_JUL_WARNING, + LTTNG_LOGLEVEL_JUL_INFO, + LTTNG_LOGLEVEL_JUL_CONFIG, + LTTNG_LOGLEVEL_JUL_FINE, + LTTNG_LOGLEVEL_JUL_FINER, + LTTNG_LOGLEVEL_JUL_FINEST, + LTTNG_LOGLEVEL_JUL_ALL, + }; + const int valid_log_level_values[] = { + 0, + -1980, + 1995 + }; + + test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_JUL_LOGGING, + loglevel_jul_value_to_name, + lttng_event_rule_jul_logging_create, + lttng_event_rule_jul_logging_set_log_level_rule, + tagged_log_level_values, + ARRAY_SIZE(tagged_log_level_values), + valid_log_level_values, + ARRAY_SIZE(valid_log_level_values), NULL, 0); +} + +static void test_event_rule_log_level_log4j(void) +{ + const int tagged_log_level_values[] = { + LTTNG_LOGLEVEL_LOG4J_OFF, + LTTNG_LOGLEVEL_LOG4J_FATAL, + LTTNG_LOGLEVEL_LOG4J_ERROR, + LTTNG_LOGLEVEL_LOG4J_WARN, + LTTNG_LOGLEVEL_LOG4J_INFO, + LTTNG_LOGLEVEL_LOG4J_DEBUG, + LTTNG_LOGLEVEL_LOG4J_TRACE, + LTTNG_LOGLEVEL_LOG4J_ALL, + }; + const int valid_log_level_values[] = { + 0 + -1980, + 1995 + }; + + test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING, + loglevel_log4j_value_to_name, + lttng_event_rule_log4j_logging_create, + lttng_event_rule_log4j_logging_set_log_level_rule, + tagged_log_level_values, + ARRAY_SIZE(tagged_log_level_values), + valid_log_level_values, + ARRAY_SIZE(valid_log_level_values), NULL, 0); +} + +static void test_event_rule_log_level_python(void) +{ + const int tagged_log_level_values[] = { + LTTNG_LOGLEVEL_PYTHON_CRITICAL, + LTTNG_LOGLEVEL_PYTHON_ERROR, + LTTNG_LOGLEVEL_PYTHON_WARNING, + LTTNG_LOGLEVEL_PYTHON_INFO, + LTTNG_LOGLEVEL_PYTHON_DEBUG, + LTTNG_LOGLEVEL_PYTHON_NOTSET, + }; + const int valid_log_level_values[] = { + 45, + 35, + 0, + -657, + }; + + test_event_rule_log_level_generic(LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING, + loglevel_python_value_to_name, + lttng_event_rule_python_logging_create, + lttng_event_rule_python_logging_set_log_level_rule, + tagged_log_level_values, + ARRAY_SIZE(tagged_log_level_values), + valid_log_level_values, + ARRAY_SIZE(valid_log_level_values), + NULL, 0); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_event_rule_kernel_tracepoint(); + test_event_rule_user_tracepoint(); + test_event_rule_syscall(); + test_event_rule_userspace_probe(); + test_event_rule_kernel_probe(); + test_event_rule_log4j_logging(); + test_event_rule_jul_logging(); + test_event_rule_python_logging(); + test_event_rule_log_level_ust(); + test_event_rule_log_level_jul(); + test_event_rule_log_level_log4j(); + test_event_rule_log_level_python(); + return exit_status(); +} diff --git a/tests/unit/test_fd_tracker.c b/tests/unit/test_fd_tracker.c deleted file mode 100644 index 6df179f9d..000000000 --- a/tests/unit/test_fd_tracker.c +++ /dev/null @@ -1,920 +0,0 @@ -/* - * Copyright (C) 2018 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -/* Number of TAP tests in this file */ -#define NUM_TESTS 61 -/* 3 for stdin, stdout, and stderr */ -#define STDIO_FD_COUNT 3 -#define TRACKER_FD_LIMIT 50 -#define TMP_DIR_PATTERN "/tmp/fd-tracker-XXXXXX" -#define TEST_UNLINK_DIRECTORY_NAME "unlinked_files" - -#ifdef __linux__ -#define SELF_FD_DIR "/proc/self/fd" -#else -/* Most Unices have /dev/fd */ -#define SELF_FD_DIR "/dev/fd" -#endif - -/* - * Count of fds, beyond stdin, stderr, stdout that were open - * at the launch of the test. This allows the test to succeed when - * run by automake's test runner or valgrind which both open - * fds behind our back. - */ -int unknown_fds_count; - -const char file_contents[] = "Bacon ipsum dolor amet jerky drumstick sirloin " - "strip steak venison boudin filet mignon picanha doner shoulder. " - "Strip steak brisket alcatra, venison beef chuck cupim pastrami. " - "Landjaeger tri-tip salami leberkas ball tip, ham hock chuck sausage " - "flank jerky cupim. Pig bacon chuck pancetta andouille."; - -static -void get_temporary_directories(char **_test_directory, char **_unlink_directory) -{ - int ret; - char tmp_path_pattern[] = TMP_DIR_PATTERN; - char *output_dir; - - output_dir = mkdtemp(tmp_path_pattern); - if (!output_dir) { - diag("Failed to create temporary path of the form %s", - TMP_DIR_PATTERN); - abort(); - } - - *_test_directory = strdup(output_dir); - LTTNG_ASSERT(*_test_directory); - ret = asprintf(_unlink_directory, "%s/%s", output_dir, - TEST_UNLINK_DIRECTORY_NAME); - if (ret < 0) { - abort(); - } -} - -static -int fd_count(void) -{ - DIR *dir; - struct dirent *entry; - int count = 0; - - dir = opendir(SELF_FD_DIR); - if (!dir) { - perror("# Failed to enumerate " SELF_FD_DIR " to count the number of used file descriptors"); - count = -1; - goto end; - } - - while ((entry = readdir(dir)) != NULL) { - if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { - continue; - } - count++; - } - /* Don't account for the file descriptor opened by opendir(). */ - count--; - if (closedir(dir)) { - perror("# Failed to close test program's " SELF_FD_DIR " directory file descriptor"); - } -end: - return count; -} - -static -void check_fd_count(int expected_count) -{ - int count = 0; - - count = fd_count(); - ok(count == expected_count, "Expected %d open file descriptors (%d are open)", - expected_count, count); -} - -static -int noop_open(void *data, int *fds) -{ - *fds = *((int *) data); - return 0; -} - -static -int noop_close(void *data, int *fds) -{ - return 0; -} - -static -void track_std_fds(struct fd_tracker *tracker) -{ - int i; - struct { int fd; const char *name; } files[] = { - { .fd = fileno(stdin), .name = "stdin" }, - { .fd = fileno(stdout), .name = "stdout" }, - { .fd = fileno(stderr), .name = "stderr" }, - }; - - for (i = 0; i < sizeof(files) / sizeof(*files); i++) { - int out_fd, ret; - - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - &files[i].name, 1, noop_open, &files[i].fd); - LTTNG_ASSERT(out_fd == files[i].fd); - - ok(ret == 0, "Track unsuspendable fd %d (%s)", files[i].fd, - files[i].name); - } -} - -static -void untrack_std_fds(struct fd_tracker *tracker) -{ - int i; - struct { int fd; const char *name; } files[] = { - { .fd = fileno(stdin), .name = "stdin" }, - { .fd = fileno(stdout), .name = "stdout" }, - { .fd = fileno(stderr), .name = "stderr" }, - }; - - for (i = 0; i < sizeof(files) / sizeof(*files); i++) { - int fd = files[i].fd; - int ret = fd_tracker_close_unsuspendable_fd(tracker, - &files[i].fd, 1, noop_close, NULL); - - ok(ret == 0, "Untrack unsuspendable fd %d (%s)", fd, - files[i].name); - } -} - -/* - * Basic test opening and closing three unsuspendable fds. - */ -static -void test_unsuspendable_basic(void) -{ - int ret; - struct fd_tracker *tracker; - char *test_directory = NULL, *unlinked_files_directory = NULL; - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - ok(tracker, "Created an fd tracker with a limit of %d simulateously opened file descriptors", - TRACKER_FD_LIMIT); - if (!tracker) { - goto end; - } - - track_std_fds(tracker); - untrack_std_fds(tracker); - - fd_tracker_destroy(tracker); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); -end: - free(test_directory); - free(unlinked_files_directory); -} - -static -int error_open(void *data, int *fds) -{ - return *((int *) data); -} - -static -int error_close(void *data, int *fds) -{ - return *((int *) data); -} - -/* - * Validate that user callback return values are returned to the - * caller of the fd tracker. - */ -static -void test_unsuspendable_cb_return(void) -{ - int ret, stdout_fd = fileno(stdout), out_fd = 42; - struct fd_tracker *tracker; - int expected_error = -ENETDOWN; - char *test_directory = NULL, *unlinked_files_directory = NULL; - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(test_directory, TRACKER_FD_LIMIT); - LTTNG_ASSERT(tracker); - - /* The error_open callback should fail and return 'expected_error'. */ - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - NULL, 1, error_open, &expected_error); - ok(ret == expected_error, "fd_tracker_open_unsuspendable_fd() forwards the user callback's error code"); - ok(out_fd == 42, "Output fd parameter is unaffected on error of fd_tracker_open_unsuspendable_fd()"); - - /* - * Track a valid fd since we don't want the tracker to fail with an - * invalid fd error for this test. - */ - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - NULL, 1, noop_open, &stdout_fd); - ok(out_fd == stdout_fd, "fd_tracker_open_unsuspendable_fd() sets the output fd parameter to the newly-tracked fd's value"); - LTTNG_ASSERT(!ret); - - ret = fd_tracker_close_unsuspendable_fd(tracker, - &stdout_fd, 1, error_close, &expected_error); - ok(ret == expected_error, "fd_tracker_close_unsuspendable_fd() forwards the user callback's error code"); - ret = fd_tracker_close_unsuspendable_fd(tracker, - &stdout_fd, 1, noop_close, &expected_error); - LTTNG_ASSERT(!ret); - - fd_tracker_destroy(tracker); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); - free(test_directory); - free(unlinked_files_directory); -} - -/* - * Validate that the tracker refuses to track two identical unsuspendable - * file descriptors. - */ -static -void test_unsuspendable_duplicate(void) -{ - int ret, stdout_fd = fileno(stdout), out_fd; - struct fd_tracker *tracker; - char *test_directory = NULL, *unlinked_files_directory = NULL; - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - LTTNG_ASSERT(tracker); - - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - NULL, 1, noop_open, &stdout_fd); - LTTNG_ASSERT(!ret); - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - NULL, 1, noop_open, &stdout_fd); - ok(ret == -EEXIST, "EEXIST reported on open of an already tracked file descriptor"); - - ret = fd_tracker_close_unsuspendable_fd(tracker, - &stdout_fd, 1, noop_close, NULL); - LTTNG_ASSERT(!ret); - - fd_tracker_destroy(tracker); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); - free(test_directory); - free(unlinked_files_directory); -} - -static -int open_pipes(void *data, int *out_fds) -{ - unsigned int i; - const unsigned int pipe_count = TRACKER_FD_LIMIT / 2; - - for (i = 0; i < pipe_count; i++) { - int ret = pipe(&out_fds[i * 2]); - - if (ret) { - return -errno; - } - } - return 0; -} - -static -int close_pipes(void *data, int *fds) -{ - int i; - int *pipes = fds; - - for (i = 0; i < TRACKER_FD_LIMIT; i++) { - int ret = close(pipes[i]); - - if (ret) { - return -errno; - } - } - return 0; -} - -/* - * Validate that the tracker enforces the open file descriptor limit - * when unsuspendable file descriptors are being opened. - */ -static -void test_unsuspendable_limit(void) -{ - struct fd_tracker *tracker; - int ret, stdout_fd = fileno(stdout), out_fd; - int fds[TRACKER_FD_LIMIT]; - char *test_directory = NULL, *unlinked_files_directory = NULL; - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - /* This test assumes TRACKER_FD_LIMIT is a multiple of 2. */ - LTTNG_ASSERT((TRACKER_FD_LIMIT % 2 == 0) && TRACKER_FD_LIMIT); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - LTTNG_ASSERT(tracker); - - ret = fd_tracker_open_unsuspendable_fd(tracker, fds, - NULL, TRACKER_FD_LIMIT, open_pipes, NULL); - ok(ret == 0, "File descriptor tracker allowed the user to meet its limit with unsuspendable file descriptors (%d)", - TRACKER_FD_LIMIT); - - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - NULL, 1, noop_open, &stdout_fd); - ok(ret == -EMFILE, "EMFILE reported when exceeding the file descriptor limit while opening an unsuspendable fd"); - - ret = fd_tracker_close_unsuspendable_fd(tracker, - fds, TRACKER_FD_LIMIT, close_pipes, NULL); - LTTNG_ASSERT(!ret); - - fd_tracker_destroy(tracker); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); - free(test_directory); - free(unlinked_files_directory); -} - -/* - * Validate that the tracker refuses to track two identical unsuspendable - * file descriptors. - */ -static -void test_unsuspendable_close_untracked(void) -{ - int ret, stdout_fd = fileno(stdout), unknown_fds[2], out_fd; - struct fd_tracker *tracker; - char *test_directory = NULL, *unlinked_files_directory = NULL; - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - if (!tracker) { - goto end;; - } - - ret = pipe(unknown_fds); - LTTNG_ASSERT(!ret); - ret = close(unknown_fds[0]); - LTTNG_ASSERT(ret == 0); - ret = close(unknown_fds[1]); - LTTNG_ASSERT(ret == 0); - - ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, - NULL, 1, noop_open, &stdout_fd); - LTTNG_ASSERT(!ret); - - ret = fd_tracker_close_unsuspendable_fd(tracker, - unknown_fds, 1, noop_close, NULL); - ok(ret == -EINVAL, "EINVAL reported on close of an untracked file descriptor"); - - ret = fd_tracker_close_unsuspendable_fd(tracker, - &stdout_fd, 1, noop_close, NULL); - LTTNG_ASSERT(!ret); - - fd_tracker_destroy(tracker); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); -end: - free(test_directory); - free(unlinked_files_directory); -} - -static int open_files(struct fd_tracker *tracker, - struct lttng_directory_handle *directory, - unsigned int count, - struct fs_handle **handles, - char **file_paths) -{ - int ret = 0; - unsigned int i; - - for (i = 0; i < count; i++) { - int p_ret; - char *file_path; - struct fs_handle *handle; - mode_t mode = S_IWUSR | S_IRUSR; - - p_ret = asprintf(&file_path, "file-%u", i); - LTTNG_ASSERT(p_ret >= 0); - file_paths[i] = file_path; - - handle = fd_tracker_open_fs_handle(tracker, directory, file_path, - O_RDWR | O_CREAT, &mode); - if (!handle) { - ret = -1; - break; - } - handles[i] = handle; - } - return ret; -} - -static int open_same_file(struct fd_tracker *tracker, - struct lttng_directory_handle *directory, - const char *file, - unsigned int count, - struct fs_handle **handles) -{ - int ret = 0; - unsigned int i; - - for (i = 0; i < count; i++) { - struct fs_handle *handle; - mode_t mode = S_IWUSR | S_IRUSR; - - handle = fd_tracker_open_fs_handle(tracker, directory, file, - O_RDWR | O_CREAT, &mode); - if (!handle) { - ret = -1; - break; - } - handles[i] = handle; - } - return ret; -} - -static -int cleanup_files(struct fd_tracker *tracker, const char *dir, - unsigned int count, struct fs_handle **handles, - char **file_paths) -{ - int ret = 0; - unsigned int i; - - for (i = 0; i < count; i++) { - char *file_path = file_paths[i]; - - if (!file_path) { - break; - } - if (fs_handle_unlink(handles[i])) { - diag("Failed to unlink fs_handle to file %s", file_path); - ret = -1; - } - if (fs_handle_close(handles[i])) { - diag("Failed to close fs_handle to file %s", file_path); - ret = -1; - } - free(file_path); - } - return ret; -} - -static -void test_suspendable_limit(void) -{ - int ret; - const int files_to_create = TRACKER_FD_LIMIT * 10; - struct fd_tracker *tracker; - char *test_directory = NULL, *unlinked_files_directory = NULL; - char *output_files[files_to_create]; - struct fs_handle *handles[files_to_create]; - struct lttng_directory_handle *dir_handle = NULL; - int dir_handle_fd_count; - - memset(output_files, 0, sizeof(output_files)); - memset(handles, 0, sizeof(handles)); - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - if (!tracker) { - goto end; - } - - dir_handle = lttng_directory_handle_create(test_directory); - LTTNG_ASSERT(dir_handle); - dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle); - - ret = open_files(tracker, dir_handle, files_to_create, handles, - output_files); - ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor", - files_to_create, TRACKER_FD_LIMIT); - check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count + - dir_handle_fd_count); - - ret = cleanup_files(tracker, test_directory, files_to_create, handles, - output_files); - ok(!ret, "Close all opened filesystem handles"); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); - fd_tracker_destroy(tracker); - lttng_directory_handle_put(dir_handle); -end: - free(test_directory); - free(unlinked_files_directory); -} - -static -void test_mixed_limit(void) -{ - int ret; - const int files_to_create = TRACKER_FD_LIMIT; - struct fd_tracker *tracker; - char *test_directory = NULL, *unlinked_files_directory = NULL; - char *output_files[files_to_create]; - struct fs_handle *handles[files_to_create]; - struct lttng_directory_handle *dir_handle = NULL; - int dir_handle_fd_count; - - memset(output_files, 0, sizeof(output_files)); - memset(handles, 0, sizeof(handles)); - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - if (!tracker) { - goto end; - } - - dir_handle = lttng_directory_handle_create(test_directory); - LTTNG_ASSERT(dir_handle); - dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle); - - ret = open_files(tracker, dir_handle, files_to_create, handles, - output_files); - ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor", - files_to_create, TRACKER_FD_LIMIT); - diag("Check file descriptor count after opening %u files", files_to_create); - check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count + - dir_handle_fd_count); - - /* - * Open unsuspendable fds (stdin, stdout, stderr) and verify that the fd - * cap is still respected. - */ - diag("Check file descriptor count after adding %d unsuspendable fds", - STDIO_FD_COUNT); - track_std_fds(tracker); - check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count + - dir_handle_fd_count); - diag("Untrack unsuspendable file descriptors"); - untrack_std_fds(tracker); - check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count + - dir_handle_fd_count); - - ret = cleanup_files(tracker, test_directory, files_to_create, handles, - output_files); - ok(!ret, "Close all opened filesystem handles"); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); - fd_tracker_destroy(tracker); - lttng_directory_handle_put(dir_handle); -end: - free(test_directory); - free(unlinked_files_directory); -} - -/* - * Open more files than allowed by the fd tracker's cap and write, - * byte-by-byte, and in round-robin, a string. The goal is to force - * the fd tracker to suspend and resume the fs_handles often and - * verify that the fd cap is always respected. - * - * The content of the files is also verified at the end. - */ -static -void test_suspendable_restore(void) -{ - int ret; - const int files_to_create = TRACKER_FD_LIMIT * 10; - struct fd_tracker *tracker; - char *output_files[files_to_create]; - struct fs_handle *handles[files_to_create]; - size_t content_index; - int handle_index; - bool write_success = true; - bool fd_cap_respected = true; - bool content_ok = true; - struct lttng_directory_handle *dir_handle = NULL; - int dir_handle_fd_count; - char *test_directory = NULL, *unlinked_files_directory = NULL; - - memset(output_files, 0, sizeof(output_files)); - memset(handles, 0, sizeof(handles)); - - get_temporary_directories(&test_directory, &unlinked_files_directory); - - tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); - if (!tracker) { - goto end; - } - - dir_handle = lttng_directory_handle_create(test_directory); - LTTNG_ASSERT(dir_handle); - dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle); - - ret = open_files(tracker, dir_handle, files_to_create, handles, - output_files); - ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor", - files_to_create, TRACKER_FD_LIMIT); - diag("Check file descriptor count after opening %u files", files_to_create); - check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count + - dir_handle_fd_count); - - for (content_index = 0; content_index < sizeof(file_contents); content_index++) { - for (handle_index = 0; handle_index < files_to_create; handle_index++) { - int fd; - struct fs_handle *handle = handles[handle_index]; - const char *path = output_files[handle_index]; - - fd = fs_handle_get_fd(handle); - if (fd < 0) { - write_success = false; - diag("Failed to restore fs_handle to %s", - path); - goto skip_write; - } - - do { - ret = write(fd, file_contents + content_index, 1); - } while (ret < 0 && errno == EINTR); - - if (ret != 1) { - write_success = false; - PERROR("write() to %s failed", path); - goto skip_write; - } - - if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT + - unknown_fds_count + - dir_handle_fd_count)) { - fd_cap_respected = false; - } - - fs_handle_put_fd(handle); - } - } -skip_write: - ok(write_success, "Wrote reference string to %d files", - files_to_create); - ok(fd_cap_respected, "FD tracker enforced the file descriptor cap"); - - /* Validate the contents of the files. */ - for (handle_index = 0; handle_index < files_to_create; handle_index++) { - struct stat fd_stat; - const char *path = output_files[handle_index]; - char read_buf[sizeof(file_contents)]; - char *read_pos; - size_t to_read = sizeof(read_buf); - int fd; - - fd = lttng_directory_handle_open_file( - dir_handle, path, O_RDONLY, 0); - LTTNG_ASSERT(fd >= 0); - ret = fstat(fd, &fd_stat); - LTTNG_ASSERT(!ret); - if (fd_stat.st_size != sizeof(file_contents)) { - diag("Content size of file %s doesn't match, got %" PRId64 ", expected %zu", - path, (int64_t) fd_stat.st_size, - sizeof(file_contents)); - content_ok = false; - (void) close(fd); - break; - } - - read_pos = read_buf; - do { - ret = read(fd, read_pos, to_read); - if (ret > 0) { - to_read -= ret; - read_pos += ret; - } - } while (to_read && (ret < 0 && errno == EINTR)); - if (ret < 0) { - content_ok = false; - PERROR("Failed to read file %s", path); - (void) close(fd); - break; - } - - if (strcmp(file_contents, read_buf)) { - content_ok = false; - diag("File content doesn't match the expectated string"); - (void) close(fd); - break; - } - (void) close(fd); - } - ok(content_ok, "Files contain the expected content"); - ret = cleanup_files(tracker, test_directory, files_to_create, handles, - output_files); - ok(!ret, "Close all opened filesystem handles"); - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); - fd_tracker_destroy(tracker); - lttng_directory_handle_put(dir_handle); -end: - free(test_directory); - free(unlinked_files_directory); -} - -static -void test_unlink(void) -{ - int ret; - struct fd_tracker *tracker; - const int handles_to_open = 2; - struct fs_handle *handles[handles_to_open]; - struct fs_handle *new_handle = NULL; - struct stat statbuf; - struct lttng_directory_handle *dir_handle = NULL; - const char file_name[] = "my_file"; - char *test_directory = NULL, *unlinked_files_directory = NULL; - char *unlinked_file_zero = NULL, *unlinked_file_one = NULL; - int fd; - - get_temporary_directories(&test_directory, &unlinked_files_directory); - ret = asprintf(&unlinked_file_zero, "%s/%u", unlinked_files_directory, - 0); - LTTNG_ASSERT(ret > 0); - ret = asprintf(&unlinked_file_one, "%s/%u", unlinked_files_directory, - 1); - LTTNG_ASSERT(ret > 0); - - tracker = fd_tracker_create(unlinked_files_directory, 1); - if (!tracker) { - goto end; - } - - dir_handle = lttng_directory_handle_create(test_directory); - LTTNG_ASSERT(dir_handle); - - /* Open two handles to the same file. */ - ret = open_same_file(tracker, dir_handle, file_name, handles_to_open, - handles); - ok(!ret, "Successfully opened %i handles to %s/%s", handles_to_open, - test_directory, file_name); - if (ret) { - goto end; - } - - /* - * Unlinking the first handle should cause the file to be renamed - * to '0'. - */ - ret = fs_handle_unlink(handles[0]); - ok(!ret, "Successfully unlinked the first handle to %s/%s", - test_directory, file_name); - - /* - * The original file should no longer exist on the file system, and a - * new file named '0' should exist. - */ - ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 && - errno == ENOENT, - "%s no longer present on file system after unlink", - file_name); - ok(lttng_directory_handle_stat( - dir_handle, unlinked_file_zero, &statbuf) == 0, - "%s exists on file system after unlink", - unlinked_file_zero); - - /* - * It should be possible to use the file descriptors of both handles. - * Since only one file descriptor can be opened at once, this should - * force the fd_tracker to suspend and restore the handles. - */ - fd = fs_handle_get_fd(handles[0]); - ok(fd >= 0, "Got fd from first handle"); - - fd = fs_handle_get_fd(handles[1]); - ok (fd < 0, "fd tracker does not allow two fds to be used at once"); - - fs_handle_put_fd(handles[0]); - fd = fs_handle_get_fd(handles[1]); - ok(fd >= 0, "Got fd from second handle"); - fs_handle_put_fd(handles[1]); - - /* The second unlink should fail with -ENOENT. */ - ret = fs_handle_unlink(handles[1]); - ok(ret == -ENOENT, - "ENOENT is reported when attempting to unlink the second handle to %s/%s", - test_directory, file_name); - - /* - * Opening a new handle to 'my_file' should succeed. - */ - ret = open_same_file(tracker, dir_handle, file_name, 1, &new_handle); - ok(!ret, "Successfully opened a new handle to previously unlinked file %s/%s", - test_directory, file_name); - LTTNG_ASSERT(new_handle); - - /* - * Unlinking the new handle should cause the file to be renamed - * to '1' since '0' already exists. - */ - ret = fs_handle_unlink(new_handle); - ok(!ret, "Successfully unlinked the new handle handle to %s/%s", - test_directory, file_name); - ok(stat(unlinked_file_one, &statbuf) == 0, - "%s exists on file system after unlink", - unlinked_file_one); - - ret = fs_handle_close(handles[0]); - ok(!ret, "Successfully closed the first handle"); - ret = fs_handle_close(handles[1]); - ok(!ret, "Successfully closed the second handle"); - ret = fs_handle_close(new_handle); - ok(!ret, "Successfully closed the third handle"); - - ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 && - errno == ENOENT, - "%s no longer present on file system after handle close", - file_name); - ok(lttng_directory_handle_stat( - dir_handle, unlinked_file_zero, &statbuf) == -1 && - errno == ENOENT, - "%s no longer present on file system after handle close", - unlinked_file_zero); - ok(lttng_directory_handle_stat(dir_handle, unlinked_file_one, - &statbuf) == -1 && - errno == ENOENT, - "%s no longer present on file system after handle close", - unlinked_file_one); - - ret = rmdir(test_directory); - ok(ret == 0, "Test directory is empty"); -end: - fd_tracker_destroy(tracker); - free(test_directory); - free(unlinked_files_directory); - free(unlinked_file_zero); - free(unlinked_file_one); - lttng_directory_handle_put(dir_handle); -} - -int main(int argc, char **argv) -{ - plan_tests(NUM_TESTS); - diag("File descriptor tracker unit tests"); - - rcu_register_thread(); - - unknown_fds_count = fd_count() - STDIO_FD_COUNT; - LTTNG_ASSERT(unknown_fds_count >= 0); - - diag("Unsuspendable - basic"); - test_unsuspendable_basic(); - diag("Unsuspendable - callback return values"); - test_unsuspendable_cb_return(); - diag("Unsuspendable - duplicate file descriptors"); - test_unsuspendable_duplicate(); - diag("Unsuspendable - closing an untracked file descriptor"); - test_unsuspendable_close_untracked(); - diag("Unsuspendable - check that file descriptor limit is enforced"); - test_unsuspendable_limit(); - - diag("Suspendable - check that file descriptor limit is enforced"); - test_suspendable_limit(); - diag("Suspendable - restoration test"); - test_suspendable_restore(); - - diag("Mixed - check that file descriptor limit is enforced"); - test_mixed_limit(); - - diag("Suspendable - Unlinking test"); - test_unlink(); - - rcu_barrier(); - rcu_unregister_thread(); - return exit_status(); -} diff --git a/tests/unit/test_fd_tracker.cpp b/tests/unit/test_fd_tracker.cpp new file mode 100644 index 000000000..6df179f9d --- /dev/null +++ b/tests/unit/test_fd_tracker.cpp @@ -0,0 +1,920 @@ +/* + * Copyright (C) 2018 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +/* Number of TAP tests in this file */ +#define NUM_TESTS 61 +/* 3 for stdin, stdout, and stderr */ +#define STDIO_FD_COUNT 3 +#define TRACKER_FD_LIMIT 50 +#define TMP_DIR_PATTERN "/tmp/fd-tracker-XXXXXX" +#define TEST_UNLINK_DIRECTORY_NAME "unlinked_files" + +#ifdef __linux__ +#define SELF_FD_DIR "/proc/self/fd" +#else +/* Most Unices have /dev/fd */ +#define SELF_FD_DIR "/dev/fd" +#endif + +/* + * Count of fds, beyond stdin, stderr, stdout that were open + * at the launch of the test. This allows the test to succeed when + * run by automake's test runner or valgrind which both open + * fds behind our back. + */ +int unknown_fds_count; + +const char file_contents[] = "Bacon ipsum dolor amet jerky drumstick sirloin " + "strip steak venison boudin filet mignon picanha doner shoulder. " + "Strip steak brisket alcatra, venison beef chuck cupim pastrami. " + "Landjaeger tri-tip salami leberkas ball tip, ham hock chuck sausage " + "flank jerky cupim. Pig bacon chuck pancetta andouille."; + +static +void get_temporary_directories(char **_test_directory, char **_unlink_directory) +{ + int ret; + char tmp_path_pattern[] = TMP_DIR_PATTERN; + char *output_dir; + + output_dir = mkdtemp(tmp_path_pattern); + if (!output_dir) { + diag("Failed to create temporary path of the form %s", + TMP_DIR_PATTERN); + abort(); + } + + *_test_directory = strdup(output_dir); + LTTNG_ASSERT(*_test_directory); + ret = asprintf(_unlink_directory, "%s/%s", output_dir, + TEST_UNLINK_DIRECTORY_NAME); + if (ret < 0) { + abort(); + } +} + +static +int fd_count(void) +{ + DIR *dir; + struct dirent *entry; + int count = 0; + + dir = opendir(SELF_FD_DIR); + if (!dir) { + perror("# Failed to enumerate " SELF_FD_DIR " to count the number of used file descriptors"); + count = -1; + goto end; + } + + while ((entry = readdir(dir)) != NULL) { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { + continue; + } + count++; + } + /* Don't account for the file descriptor opened by opendir(). */ + count--; + if (closedir(dir)) { + perror("# Failed to close test program's " SELF_FD_DIR " directory file descriptor"); + } +end: + return count; +} + +static +void check_fd_count(int expected_count) +{ + int count = 0; + + count = fd_count(); + ok(count == expected_count, "Expected %d open file descriptors (%d are open)", + expected_count, count); +} + +static +int noop_open(void *data, int *fds) +{ + *fds = *((int *) data); + return 0; +} + +static +int noop_close(void *data, int *fds) +{ + return 0; +} + +static +void track_std_fds(struct fd_tracker *tracker) +{ + int i; + struct { int fd; const char *name; } files[] = { + { .fd = fileno(stdin), .name = "stdin" }, + { .fd = fileno(stdout), .name = "stdout" }, + { .fd = fileno(stderr), .name = "stderr" }, + }; + + for (i = 0; i < sizeof(files) / sizeof(*files); i++) { + int out_fd, ret; + + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + &files[i].name, 1, noop_open, &files[i].fd); + LTTNG_ASSERT(out_fd == files[i].fd); + + ok(ret == 0, "Track unsuspendable fd %d (%s)", files[i].fd, + files[i].name); + } +} + +static +void untrack_std_fds(struct fd_tracker *tracker) +{ + int i; + struct { int fd; const char *name; } files[] = { + { .fd = fileno(stdin), .name = "stdin" }, + { .fd = fileno(stdout), .name = "stdout" }, + { .fd = fileno(stderr), .name = "stderr" }, + }; + + for (i = 0; i < sizeof(files) / sizeof(*files); i++) { + int fd = files[i].fd; + int ret = fd_tracker_close_unsuspendable_fd(tracker, + &files[i].fd, 1, noop_close, NULL); + + ok(ret == 0, "Untrack unsuspendable fd %d (%s)", fd, + files[i].name); + } +} + +/* + * Basic test opening and closing three unsuspendable fds. + */ +static +void test_unsuspendable_basic(void) +{ + int ret; + struct fd_tracker *tracker; + char *test_directory = NULL, *unlinked_files_directory = NULL; + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + ok(tracker, "Created an fd tracker with a limit of %d simulateously opened file descriptors", + TRACKER_FD_LIMIT); + if (!tracker) { + goto end; + } + + track_std_fds(tracker); + untrack_std_fds(tracker); + + fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); +end: + free(test_directory); + free(unlinked_files_directory); +} + +static +int error_open(void *data, int *fds) +{ + return *((int *) data); +} + +static +int error_close(void *data, int *fds) +{ + return *((int *) data); +} + +/* + * Validate that user callback return values are returned to the + * caller of the fd tracker. + */ +static +void test_unsuspendable_cb_return(void) +{ + int ret, stdout_fd = fileno(stdout), out_fd = 42; + struct fd_tracker *tracker; + int expected_error = -ENETDOWN; + char *test_directory = NULL, *unlinked_files_directory = NULL; + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(test_directory, TRACKER_FD_LIMIT); + LTTNG_ASSERT(tracker); + + /* The error_open callback should fail and return 'expected_error'. */ + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + NULL, 1, error_open, &expected_error); + ok(ret == expected_error, "fd_tracker_open_unsuspendable_fd() forwards the user callback's error code"); + ok(out_fd == 42, "Output fd parameter is unaffected on error of fd_tracker_open_unsuspendable_fd()"); + + /* + * Track a valid fd since we don't want the tracker to fail with an + * invalid fd error for this test. + */ + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + NULL, 1, noop_open, &stdout_fd); + ok(out_fd == stdout_fd, "fd_tracker_open_unsuspendable_fd() sets the output fd parameter to the newly-tracked fd's value"); + LTTNG_ASSERT(!ret); + + ret = fd_tracker_close_unsuspendable_fd(tracker, + &stdout_fd, 1, error_close, &expected_error); + ok(ret == expected_error, "fd_tracker_close_unsuspendable_fd() forwards the user callback's error code"); + ret = fd_tracker_close_unsuspendable_fd(tracker, + &stdout_fd, 1, noop_close, &expected_error); + LTTNG_ASSERT(!ret); + + fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + free(test_directory); + free(unlinked_files_directory); +} + +/* + * Validate that the tracker refuses to track two identical unsuspendable + * file descriptors. + */ +static +void test_unsuspendable_duplicate(void) +{ + int ret, stdout_fd = fileno(stdout), out_fd; + struct fd_tracker *tracker; + char *test_directory = NULL, *unlinked_files_directory = NULL; + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + LTTNG_ASSERT(tracker); + + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + NULL, 1, noop_open, &stdout_fd); + LTTNG_ASSERT(!ret); + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + NULL, 1, noop_open, &stdout_fd); + ok(ret == -EEXIST, "EEXIST reported on open of an already tracked file descriptor"); + + ret = fd_tracker_close_unsuspendable_fd(tracker, + &stdout_fd, 1, noop_close, NULL); + LTTNG_ASSERT(!ret); + + fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + free(test_directory); + free(unlinked_files_directory); +} + +static +int open_pipes(void *data, int *out_fds) +{ + unsigned int i; + const unsigned int pipe_count = TRACKER_FD_LIMIT / 2; + + for (i = 0; i < pipe_count; i++) { + int ret = pipe(&out_fds[i * 2]); + + if (ret) { + return -errno; + } + } + return 0; +} + +static +int close_pipes(void *data, int *fds) +{ + int i; + int *pipes = fds; + + for (i = 0; i < TRACKER_FD_LIMIT; i++) { + int ret = close(pipes[i]); + + if (ret) { + return -errno; + } + } + return 0; +} + +/* + * Validate that the tracker enforces the open file descriptor limit + * when unsuspendable file descriptors are being opened. + */ +static +void test_unsuspendable_limit(void) +{ + struct fd_tracker *tracker; + int ret, stdout_fd = fileno(stdout), out_fd; + int fds[TRACKER_FD_LIMIT]; + char *test_directory = NULL, *unlinked_files_directory = NULL; + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + /* This test assumes TRACKER_FD_LIMIT is a multiple of 2. */ + LTTNG_ASSERT((TRACKER_FD_LIMIT % 2 == 0) && TRACKER_FD_LIMIT); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + LTTNG_ASSERT(tracker); + + ret = fd_tracker_open_unsuspendable_fd(tracker, fds, + NULL, TRACKER_FD_LIMIT, open_pipes, NULL); + ok(ret == 0, "File descriptor tracker allowed the user to meet its limit with unsuspendable file descriptors (%d)", + TRACKER_FD_LIMIT); + + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + NULL, 1, noop_open, &stdout_fd); + ok(ret == -EMFILE, "EMFILE reported when exceeding the file descriptor limit while opening an unsuspendable fd"); + + ret = fd_tracker_close_unsuspendable_fd(tracker, + fds, TRACKER_FD_LIMIT, close_pipes, NULL); + LTTNG_ASSERT(!ret); + + fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + free(test_directory); + free(unlinked_files_directory); +} + +/* + * Validate that the tracker refuses to track two identical unsuspendable + * file descriptors. + */ +static +void test_unsuspendable_close_untracked(void) +{ + int ret, stdout_fd = fileno(stdout), unknown_fds[2], out_fd; + struct fd_tracker *tracker; + char *test_directory = NULL, *unlinked_files_directory = NULL; + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + if (!tracker) { + goto end;; + } + + ret = pipe(unknown_fds); + LTTNG_ASSERT(!ret); + ret = close(unknown_fds[0]); + LTTNG_ASSERT(ret == 0); + ret = close(unknown_fds[1]); + LTTNG_ASSERT(ret == 0); + + ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd, + NULL, 1, noop_open, &stdout_fd); + LTTNG_ASSERT(!ret); + + ret = fd_tracker_close_unsuspendable_fd(tracker, + unknown_fds, 1, noop_close, NULL); + ok(ret == -EINVAL, "EINVAL reported on close of an untracked file descriptor"); + + ret = fd_tracker_close_unsuspendable_fd(tracker, + &stdout_fd, 1, noop_close, NULL); + LTTNG_ASSERT(!ret); + + fd_tracker_destroy(tracker); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); +end: + free(test_directory); + free(unlinked_files_directory); +} + +static int open_files(struct fd_tracker *tracker, + struct lttng_directory_handle *directory, + unsigned int count, + struct fs_handle **handles, + char **file_paths) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < count; i++) { + int p_ret; + char *file_path; + struct fs_handle *handle; + mode_t mode = S_IWUSR | S_IRUSR; + + p_ret = asprintf(&file_path, "file-%u", i); + LTTNG_ASSERT(p_ret >= 0); + file_paths[i] = file_path; + + handle = fd_tracker_open_fs_handle(tracker, directory, file_path, + O_RDWR | O_CREAT, &mode); + if (!handle) { + ret = -1; + break; + } + handles[i] = handle; + } + return ret; +} + +static int open_same_file(struct fd_tracker *tracker, + struct lttng_directory_handle *directory, + const char *file, + unsigned int count, + struct fs_handle **handles) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < count; i++) { + struct fs_handle *handle; + mode_t mode = S_IWUSR | S_IRUSR; + + handle = fd_tracker_open_fs_handle(tracker, directory, file, + O_RDWR | O_CREAT, &mode); + if (!handle) { + ret = -1; + break; + } + handles[i] = handle; + } + return ret; +} + +static +int cleanup_files(struct fd_tracker *tracker, const char *dir, + unsigned int count, struct fs_handle **handles, + char **file_paths) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < count; i++) { + char *file_path = file_paths[i]; + + if (!file_path) { + break; + } + if (fs_handle_unlink(handles[i])) { + diag("Failed to unlink fs_handle to file %s", file_path); + ret = -1; + } + if (fs_handle_close(handles[i])) { + diag("Failed to close fs_handle to file %s", file_path); + ret = -1; + } + free(file_path); + } + return ret; +} + +static +void test_suspendable_limit(void) +{ + int ret; + const int files_to_create = TRACKER_FD_LIMIT * 10; + struct fd_tracker *tracker; + char *test_directory = NULL, *unlinked_files_directory = NULL; + char *output_files[files_to_create]; + struct fs_handle *handles[files_to_create]; + struct lttng_directory_handle *dir_handle = NULL; + int dir_handle_fd_count; + + memset(output_files, 0, sizeof(output_files)); + memset(handles, 0, sizeof(handles)); + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + if (!tracker) { + goto end; + } + + dir_handle = lttng_directory_handle_create(test_directory); + LTTNG_ASSERT(dir_handle); + dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle); + + ret = open_files(tracker, dir_handle, files_to_create, handles, + output_files); + ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor", + files_to_create, TRACKER_FD_LIMIT); + check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count + + dir_handle_fd_count); + + ret = cleanup_files(tracker, test_directory, files_to_create, handles, + output_files); + ok(!ret, "Close all opened filesystem handles"); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + fd_tracker_destroy(tracker); + lttng_directory_handle_put(dir_handle); +end: + free(test_directory); + free(unlinked_files_directory); +} + +static +void test_mixed_limit(void) +{ + int ret; + const int files_to_create = TRACKER_FD_LIMIT; + struct fd_tracker *tracker; + char *test_directory = NULL, *unlinked_files_directory = NULL; + char *output_files[files_to_create]; + struct fs_handle *handles[files_to_create]; + struct lttng_directory_handle *dir_handle = NULL; + int dir_handle_fd_count; + + memset(output_files, 0, sizeof(output_files)); + memset(handles, 0, sizeof(handles)); + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + if (!tracker) { + goto end; + } + + dir_handle = lttng_directory_handle_create(test_directory); + LTTNG_ASSERT(dir_handle); + dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle); + + ret = open_files(tracker, dir_handle, files_to_create, handles, + output_files); + ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor", + files_to_create, TRACKER_FD_LIMIT); + diag("Check file descriptor count after opening %u files", files_to_create); + check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count + + dir_handle_fd_count); + + /* + * Open unsuspendable fds (stdin, stdout, stderr) and verify that the fd + * cap is still respected. + */ + diag("Check file descriptor count after adding %d unsuspendable fds", + STDIO_FD_COUNT); + track_std_fds(tracker); + check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count + + dir_handle_fd_count); + diag("Untrack unsuspendable file descriptors"); + untrack_std_fds(tracker); + check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count + + dir_handle_fd_count); + + ret = cleanup_files(tracker, test_directory, files_to_create, handles, + output_files); + ok(!ret, "Close all opened filesystem handles"); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + fd_tracker_destroy(tracker); + lttng_directory_handle_put(dir_handle); +end: + free(test_directory); + free(unlinked_files_directory); +} + +/* + * Open more files than allowed by the fd tracker's cap and write, + * byte-by-byte, and in round-robin, a string. The goal is to force + * the fd tracker to suspend and resume the fs_handles often and + * verify that the fd cap is always respected. + * + * The content of the files is also verified at the end. + */ +static +void test_suspendable_restore(void) +{ + int ret; + const int files_to_create = TRACKER_FD_LIMIT * 10; + struct fd_tracker *tracker; + char *output_files[files_to_create]; + struct fs_handle *handles[files_to_create]; + size_t content_index; + int handle_index; + bool write_success = true; + bool fd_cap_respected = true; + bool content_ok = true; + struct lttng_directory_handle *dir_handle = NULL; + int dir_handle_fd_count; + char *test_directory = NULL, *unlinked_files_directory = NULL; + + memset(output_files, 0, sizeof(output_files)); + memset(handles, 0, sizeof(handles)); + + get_temporary_directories(&test_directory, &unlinked_files_directory); + + tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT); + if (!tracker) { + goto end; + } + + dir_handle = lttng_directory_handle_create(test_directory); + LTTNG_ASSERT(dir_handle); + dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle); + + ret = open_files(tracker, dir_handle, files_to_create, handles, + output_files); + ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor", + files_to_create, TRACKER_FD_LIMIT); + diag("Check file descriptor count after opening %u files", files_to_create); + check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count + + dir_handle_fd_count); + + for (content_index = 0; content_index < sizeof(file_contents); content_index++) { + for (handle_index = 0; handle_index < files_to_create; handle_index++) { + int fd; + struct fs_handle *handle = handles[handle_index]; + const char *path = output_files[handle_index]; + + fd = fs_handle_get_fd(handle); + if (fd < 0) { + write_success = false; + diag("Failed to restore fs_handle to %s", + path); + goto skip_write; + } + + do { + ret = write(fd, file_contents + content_index, 1); + } while (ret < 0 && errno == EINTR); + + if (ret != 1) { + write_success = false; + PERROR("write() to %s failed", path); + goto skip_write; + } + + if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT + + unknown_fds_count + + dir_handle_fd_count)) { + fd_cap_respected = false; + } + + fs_handle_put_fd(handle); + } + } +skip_write: + ok(write_success, "Wrote reference string to %d files", + files_to_create); + ok(fd_cap_respected, "FD tracker enforced the file descriptor cap"); + + /* Validate the contents of the files. */ + for (handle_index = 0; handle_index < files_to_create; handle_index++) { + struct stat fd_stat; + const char *path = output_files[handle_index]; + char read_buf[sizeof(file_contents)]; + char *read_pos; + size_t to_read = sizeof(read_buf); + int fd; + + fd = lttng_directory_handle_open_file( + dir_handle, path, O_RDONLY, 0); + LTTNG_ASSERT(fd >= 0); + ret = fstat(fd, &fd_stat); + LTTNG_ASSERT(!ret); + if (fd_stat.st_size != sizeof(file_contents)) { + diag("Content size of file %s doesn't match, got %" PRId64 ", expected %zu", + path, (int64_t) fd_stat.st_size, + sizeof(file_contents)); + content_ok = false; + (void) close(fd); + break; + } + + read_pos = read_buf; + do { + ret = read(fd, read_pos, to_read); + if (ret > 0) { + to_read -= ret; + read_pos += ret; + } + } while (to_read && (ret < 0 && errno == EINTR)); + if (ret < 0) { + content_ok = false; + PERROR("Failed to read file %s", path); + (void) close(fd); + break; + } + + if (strcmp(file_contents, read_buf)) { + content_ok = false; + diag("File content doesn't match the expectated string"); + (void) close(fd); + break; + } + (void) close(fd); + } + ok(content_ok, "Files contain the expected content"); + ret = cleanup_files(tracker, test_directory, files_to_create, handles, + output_files); + ok(!ret, "Close all opened filesystem handles"); + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); + fd_tracker_destroy(tracker); + lttng_directory_handle_put(dir_handle); +end: + free(test_directory); + free(unlinked_files_directory); +} + +static +void test_unlink(void) +{ + int ret; + struct fd_tracker *tracker; + const int handles_to_open = 2; + struct fs_handle *handles[handles_to_open]; + struct fs_handle *new_handle = NULL; + struct stat statbuf; + struct lttng_directory_handle *dir_handle = NULL; + const char file_name[] = "my_file"; + char *test_directory = NULL, *unlinked_files_directory = NULL; + char *unlinked_file_zero = NULL, *unlinked_file_one = NULL; + int fd; + + get_temporary_directories(&test_directory, &unlinked_files_directory); + ret = asprintf(&unlinked_file_zero, "%s/%u", unlinked_files_directory, + 0); + LTTNG_ASSERT(ret > 0); + ret = asprintf(&unlinked_file_one, "%s/%u", unlinked_files_directory, + 1); + LTTNG_ASSERT(ret > 0); + + tracker = fd_tracker_create(unlinked_files_directory, 1); + if (!tracker) { + goto end; + } + + dir_handle = lttng_directory_handle_create(test_directory); + LTTNG_ASSERT(dir_handle); + + /* Open two handles to the same file. */ + ret = open_same_file(tracker, dir_handle, file_name, handles_to_open, + handles); + ok(!ret, "Successfully opened %i handles to %s/%s", handles_to_open, + test_directory, file_name); + if (ret) { + goto end; + } + + /* + * Unlinking the first handle should cause the file to be renamed + * to '0'. + */ + ret = fs_handle_unlink(handles[0]); + ok(!ret, "Successfully unlinked the first handle to %s/%s", + test_directory, file_name); + + /* + * The original file should no longer exist on the file system, and a + * new file named '0' should exist. + */ + ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 && + errno == ENOENT, + "%s no longer present on file system after unlink", + file_name); + ok(lttng_directory_handle_stat( + dir_handle, unlinked_file_zero, &statbuf) == 0, + "%s exists on file system after unlink", + unlinked_file_zero); + + /* + * It should be possible to use the file descriptors of both handles. + * Since only one file descriptor can be opened at once, this should + * force the fd_tracker to suspend and restore the handles. + */ + fd = fs_handle_get_fd(handles[0]); + ok(fd >= 0, "Got fd from first handle"); + + fd = fs_handle_get_fd(handles[1]); + ok (fd < 0, "fd tracker does not allow two fds to be used at once"); + + fs_handle_put_fd(handles[0]); + fd = fs_handle_get_fd(handles[1]); + ok(fd >= 0, "Got fd from second handle"); + fs_handle_put_fd(handles[1]); + + /* The second unlink should fail with -ENOENT. */ + ret = fs_handle_unlink(handles[1]); + ok(ret == -ENOENT, + "ENOENT is reported when attempting to unlink the second handle to %s/%s", + test_directory, file_name); + + /* + * Opening a new handle to 'my_file' should succeed. + */ + ret = open_same_file(tracker, dir_handle, file_name, 1, &new_handle); + ok(!ret, "Successfully opened a new handle to previously unlinked file %s/%s", + test_directory, file_name); + LTTNG_ASSERT(new_handle); + + /* + * Unlinking the new handle should cause the file to be renamed + * to '1' since '0' already exists. + */ + ret = fs_handle_unlink(new_handle); + ok(!ret, "Successfully unlinked the new handle handle to %s/%s", + test_directory, file_name); + ok(stat(unlinked_file_one, &statbuf) == 0, + "%s exists on file system after unlink", + unlinked_file_one); + + ret = fs_handle_close(handles[0]); + ok(!ret, "Successfully closed the first handle"); + ret = fs_handle_close(handles[1]); + ok(!ret, "Successfully closed the second handle"); + ret = fs_handle_close(new_handle); + ok(!ret, "Successfully closed the third handle"); + + ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 && + errno == ENOENT, + "%s no longer present on file system after handle close", + file_name); + ok(lttng_directory_handle_stat( + dir_handle, unlinked_file_zero, &statbuf) == -1 && + errno == ENOENT, + "%s no longer present on file system after handle close", + unlinked_file_zero); + ok(lttng_directory_handle_stat(dir_handle, unlinked_file_one, + &statbuf) == -1 && + errno == ENOENT, + "%s no longer present on file system after handle close", + unlinked_file_one); + + ret = rmdir(test_directory); + ok(ret == 0, "Test directory is empty"); +end: + fd_tracker_destroy(tracker); + free(test_directory); + free(unlinked_files_directory); + free(unlinked_file_zero); + free(unlinked_file_one); + lttng_directory_handle_put(dir_handle); +} + +int main(int argc, char **argv) +{ + plan_tests(NUM_TESTS); + diag("File descriptor tracker unit tests"); + + rcu_register_thread(); + + unknown_fds_count = fd_count() - STDIO_FD_COUNT; + LTTNG_ASSERT(unknown_fds_count >= 0); + + diag("Unsuspendable - basic"); + test_unsuspendable_basic(); + diag("Unsuspendable - callback return values"); + test_unsuspendable_cb_return(); + diag("Unsuspendable - duplicate file descriptors"); + test_unsuspendable_duplicate(); + diag("Unsuspendable - closing an untracked file descriptor"); + test_unsuspendable_close_untracked(); + diag("Unsuspendable - check that file descriptor limit is enforced"); + test_unsuspendable_limit(); + + diag("Suspendable - check that file descriptor limit is enforced"); + test_suspendable_limit(); + diag("Suspendable - restoration test"); + test_suspendable_restore(); + + diag("Mixed - check that file descriptor limit is enforced"); + test_mixed_limit(); + + diag("Suspendable - Unlinking test"); + test_unlink(); + + rcu_barrier(); + rcu_unregister_thread(); + return exit_status(); +} diff --git a/tests/unit/test_kernel_probe.c b/tests/unit/test_kernel_probe.c deleted file mode 100644 index 0f6f89ec9..000000000 --- a/tests/unit/test_kernel_probe.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Unit tests for the kernel probe location API. - * - * Copyright (C) 2020 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 24 - -static void test_kernel_probe_location_address(void) -{ - struct lttng_kernel_probe_location *location = NULL; - struct lttng_kernel_probe_location *location_from_buffer = NULL; - enum lttng_kernel_probe_location_status status; - enum lttng_kernel_probe_location_type type; - uint64_t address = 50, _address; - struct lttng_payload payload; - - diag("Testing kernel probe location address"); - - lttng_payload_init(&payload); - - location = lttng_kernel_probe_location_address_create(address); - ok(location, "Location object"); - - type = lttng_kernel_probe_location_get_type(location); - ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS == type, - "Location type got %d expected %d", type, - LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); - - status = lttng_kernel_probe_location_address_get_address( - location, &_address); - ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting address"); - ok(address == _address, - "Address is equal. Got %" PRIu64 " expected %" PRIu64, - _address, address); - - ok(lttng_kernel_probe_location_serialize(location, &payload) > 0, - "Serializing"); - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - ok(lttng_kernel_probe_location_create_from_payload( - &view, &location_from_buffer) > 0, - "Deserializing"); - } - - type = lttng_kernel_probe_location_get_type(location_from_buffer); - ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS == type, - "Location from buffer type got %d expected %d", type, - LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); - - status = lttng_kernel_probe_location_address_get_address( - location_from_buffer, &_address); - ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting address"); - ok(address == _address, - "Address from buffer is equal. Got %" PRIu64 - " expected %" PRIu64, - _address, address); - - ok(lttng_kernel_probe_location_is_equal(location, location_from_buffer), - "serialized and from buffer are equal"); - - lttng_payload_reset(&payload); - lttng_kernel_probe_location_destroy(location); - lttng_kernel_probe_location_destroy(location_from_buffer); -} - -static void test_kernel_probe_location_symbol(void) -{ - struct lttng_kernel_probe_location *location = NULL; - struct lttng_kernel_probe_location *location_from_buffer = NULL; - enum lttng_kernel_probe_location_status status; - enum lttng_kernel_probe_location_type type; - uint64_t offset = 50, _offset; - const char *symbol = "Une_bonne", *_symbol; - struct lttng_payload payload; - - diag("Testing kernel probe location symbol"); - - lttng_payload_init(&payload); - - location = lttng_kernel_probe_location_symbol_create(symbol, offset); - ok(location, "Location object"); - - type = lttng_kernel_probe_location_get_type(location); - ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET == type, - "Location type got %d expected %d", type, - LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); - - _symbol = lttng_kernel_probe_location_symbol_get_name(location); - ok(_symbol, "Getting symbol name"); - ok(!strncmp(symbol, _symbol, strlen(symbol)), - "Symbol name is equal. Got %s, expected %s", _symbol, - symbol); - - status = lttng_kernel_probe_location_symbol_get_offset( - location, &_offset); - ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting offset"); - ok(offset == _offset, - "Offset is equal. Got %" PRIu64 " expected %" PRIu64, - _offset, offset); - - ok(lttng_kernel_probe_location_serialize(location, &payload) > 0, - "Serializing"); - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - ok(lttng_kernel_probe_location_create_from_payload( - &view, &location_from_buffer) > 0, - "Deserializing"); - } - - type = lttng_kernel_probe_location_get_type(location_from_buffer); - ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET == type, - "Location from buffer type got %d expected %d", type, - LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); - - _symbol = lttng_kernel_probe_location_symbol_get_name( - location_from_buffer); - ok(_symbol, "Getting symbol name"); - ok(!strncmp(symbol, _symbol, strlen(symbol)), - "Symbol name is equal. Got %s, expected %s", _symbol, - symbol); - - status = lttng_kernel_probe_location_symbol_get_offset( - location_from_buffer, &_offset); - ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting offset"); - ok(offset == _offset, - "Offset is equal. Got %" PRIu64 " expected %" PRIu64, - _offset, offset); - - ok(lttng_kernel_probe_location_is_equal(location, location_from_buffer), - "serialized and from buffer are equal"); - - lttng_payload_reset(&payload); - lttng_kernel_probe_location_destroy(location); - lttng_kernel_probe_location_destroy(location_from_buffer); -} - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_kernel_probe_location_address(); - test_kernel_probe_location_symbol(); - return exit_status(); -} diff --git a/tests/unit/test_kernel_probe.cpp b/tests/unit/test_kernel_probe.cpp new file mode 100644 index 000000000..0f6f89ec9 --- /dev/null +++ b/tests/unit/test_kernel_probe.cpp @@ -0,0 +1,168 @@ +/* + * Unit tests for the kernel probe location API. + * + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 24 + +static void test_kernel_probe_location_address(void) +{ + struct lttng_kernel_probe_location *location = NULL; + struct lttng_kernel_probe_location *location_from_buffer = NULL; + enum lttng_kernel_probe_location_status status; + enum lttng_kernel_probe_location_type type; + uint64_t address = 50, _address; + struct lttng_payload payload; + + diag("Testing kernel probe location address"); + + lttng_payload_init(&payload); + + location = lttng_kernel_probe_location_address_create(address); + ok(location, "Location object"); + + type = lttng_kernel_probe_location_get_type(location); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS == type, + "Location type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + status = lttng_kernel_probe_location_address_get_address( + location, &_address); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting address"); + ok(address == _address, + "Address is equal. Got %" PRIu64 " expected %" PRIu64, + _address, address); + + ok(lttng_kernel_probe_location_serialize(location, &payload) > 0, + "Serializing"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + ok(lttng_kernel_probe_location_create_from_payload( + &view, &location_from_buffer) > 0, + "Deserializing"); + } + + type = lttng_kernel_probe_location_get_type(location_from_buffer); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS == type, + "Location from buffer type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + status = lttng_kernel_probe_location_address_get_address( + location_from_buffer, &_address); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting address"); + ok(address == _address, + "Address from buffer is equal. Got %" PRIu64 + " expected %" PRIu64, + _address, address); + + ok(lttng_kernel_probe_location_is_equal(location, location_from_buffer), + "serialized and from buffer are equal"); + + lttng_payload_reset(&payload); + lttng_kernel_probe_location_destroy(location); + lttng_kernel_probe_location_destroy(location_from_buffer); +} + +static void test_kernel_probe_location_symbol(void) +{ + struct lttng_kernel_probe_location *location = NULL; + struct lttng_kernel_probe_location *location_from_buffer = NULL; + enum lttng_kernel_probe_location_status status; + enum lttng_kernel_probe_location_type type; + uint64_t offset = 50, _offset; + const char *symbol = "Une_bonne", *_symbol; + struct lttng_payload payload; + + diag("Testing kernel probe location symbol"); + + lttng_payload_init(&payload); + + location = lttng_kernel_probe_location_symbol_create(symbol, offset); + ok(location, "Location object"); + + type = lttng_kernel_probe_location_get_type(location); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET == type, + "Location type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + _symbol = lttng_kernel_probe_location_symbol_get_name(location); + ok(_symbol, "Getting symbol name"); + ok(!strncmp(symbol, _symbol, strlen(symbol)), + "Symbol name is equal. Got %s, expected %s", _symbol, + symbol); + + status = lttng_kernel_probe_location_symbol_get_offset( + location, &_offset); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting offset"); + ok(offset == _offset, + "Offset is equal. Got %" PRIu64 " expected %" PRIu64, + _offset, offset); + + ok(lttng_kernel_probe_location_serialize(location, &payload) > 0, + "Serializing"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + ok(lttng_kernel_probe_location_create_from_payload( + &view, &location_from_buffer) > 0, + "Deserializing"); + } + + type = lttng_kernel_probe_location_get_type(location_from_buffer); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET == type, + "Location from buffer type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + _symbol = lttng_kernel_probe_location_symbol_get_name( + location_from_buffer); + ok(_symbol, "Getting symbol name"); + ok(!strncmp(symbol, _symbol, strlen(symbol)), + "Symbol name is equal. Got %s, expected %s", _symbol, + symbol); + + status = lttng_kernel_probe_location_symbol_get_offset( + location_from_buffer, &_offset); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting offset"); + ok(offset == _offset, + "Offset is equal. Got %" PRIu64 " expected %" PRIu64, + _offset, offset); + + ok(lttng_kernel_probe_location_is_equal(location, location_from_buffer), + "serialized and from buffer are equal"); + + lttng_payload_reset(&payload); + lttng_kernel_probe_location_destroy(location); + lttng_kernel_probe_location_destroy(location_from_buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_kernel_probe_location_address(); + test_kernel_probe_location_symbol(); + return exit_status(); +} diff --git a/tests/unit/test_log_level_rule.c b/tests/unit/test_log_level_rule.c deleted file mode 100644 index d2895c811..000000000 --- a/tests/unit/test_log_level_rule.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Unit tests for the log level rule API. - * - * Copyright (C) 2020 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -/* For error.h. */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 29 - -static void test_log_level_rule_error(void) -{ - int level = 9000; - struct lttng_log_level_rule *exactly = - lttng_log_level_rule_exactly_create(level); - struct lttng_log_level_rule *at_least_as_severe = - lttng_log_level_rule_at_least_as_severe_as_create( - level); - - ok(lttng_log_level_rule_get_type(NULL) == LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN, "Get type on invalid pointer"); - - ok(lttng_log_level_rule_exactly_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, NULL) returns invalid"); - ok(lttng_log_level_rule_exactly_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (valid, NULL) returns invalid"); - ok(lttng_log_level_rule_exactly_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, valid) returns invalid"); - - ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, NULL) returns invalid"); - ok(lttng_log_level_rule_at_least_as_severe_as_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (valid, NULL) returns invalid"); - ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, valid) returns invalid"); - - lttng_log_level_rule_destroy(exactly); - lttng_log_level_rule_destroy(at_least_as_severe); -} - -static -void test_log_level_rule_serialize_deserialize(const struct lttng_log_level_rule *rule) -{ - struct lttng_log_level_rule *log_level_rule_from_buffer = NULL; - struct lttng_payload payload; - - lttng_payload_init(&payload); - - ok(lttng_log_level_rule_serialize(rule, &payload) == 0, "Serializing."); - - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_log_level_rule_create_from_payload( - &view, &log_level_rule_from_buffer) > 0, - "Deserializing."); - } - - ok(lttng_log_level_rule_is_equal(rule, log_level_rule_from_buffer), "Serialized and from buffer are equal"); - - lttng_log_level_rule_destroy(log_level_rule_from_buffer); -} - -static -void test_log_level_rule_is_equal_exactly(void) -{ - int level = 9000, no_eq_level = 420; - struct lttng_log_level_rule *a, *b, *different_level, *different_type; - - /* Identical log level rules. */ - a = lttng_log_level_rule_exactly_create(level); - b = lttng_log_level_rule_exactly_create(level); - - /* Different level, same type. */ - different_level = lttng_log_level_rule_exactly_create(no_eq_level); - - /* Different type. */ - different_type = lttng_log_level_rule_at_least_as_severe_as_create(level); - - LTTNG_ASSERT(a && b && different_level && different_type); - - ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); - ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); - ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); - ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); - - lttng_log_level_rule_destroy(a); - lttng_log_level_rule_destroy(b); - lttng_log_level_rule_destroy(different_level); - lttng_log_level_rule_destroy(different_type); -} - -static -void test_log_level_rule_is_equal_at_least_as_severe_as(void) -{ - int level = 9000, no_eq_level = 420; - struct lttng_log_level_rule *a, *b, *different_level, *different_type; - - /* Identical log level rules. */ - a = lttng_log_level_rule_at_least_as_severe_as_create(level); - b = lttng_log_level_rule_at_least_as_severe_as_create(level); - - /* Different level, same type. */ - different_level = lttng_log_level_rule_at_least_as_severe_as_create(no_eq_level); - - /* Different type. */ - different_type = lttng_log_level_rule_exactly_create(level); - - LTTNG_ASSERT(a && b && different_level && different_type); - - ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); - ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); - ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); - ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); - - lttng_log_level_rule_destroy(a); - lttng_log_level_rule_destroy(b); - lttng_log_level_rule_destroy(different_level); - lttng_log_level_rule_destroy(different_type); -} - -static void test_log_level_rule_exactly(void) -{ - int level = 9000; - int _level; - struct lttng_log_level_rule *exactly = NULL; - enum lttng_log_level_rule_status status; - - exactly = lttng_log_level_rule_exactly_create(level); - - ok(exactly, "Log level exactly allocated"); - ok(lttng_log_level_rule_get_type(exactly) == - LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY, - "Log level rule exactly type"); - - status = lttng_log_level_rule_exactly_get_level(exactly, &_level); - ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); - ok(_level == level, "Level property is valid"); - - test_log_level_rule_is_equal_exactly(); - test_log_level_rule_serialize_deserialize(exactly); - lttng_log_level_rule_destroy(exactly); -} - -static void test_log_level_rule_at_least_as_severe_as(void) -{ - int level = 9000; - int _level; - struct lttng_log_level_rule *at_least_as_severe_as = NULL; - enum lttng_log_level_rule_status status; - - at_least_as_severe_as = lttng_log_level_rule_at_least_as_severe_as_create(level); - - ok(at_least_as_severe_as, "Log level at_least_as_severe_as allocated"); - ok(lttng_log_level_rule_get_type(at_least_as_severe_as) == - LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS, - "Log level rule at_least_as_severe_as type"); - - status = lttng_log_level_rule_at_least_as_severe_as_get_level(at_least_as_severe_as, &_level); - ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); - ok(_level == level, "Level property is valid"); - - test_log_level_rule_is_equal_at_least_as_severe_as(); - test_log_level_rule_serialize_deserialize(at_least_as_severe_as); - lttng_log_level_rule_destroy(at_least_as_severe_as); -} - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_log_level_rule_exactly(); - test_log_level_rule_at_least_as_severe_as(); - test_log_level_rule_error(); - return exit_status(); -} diff --git a/tests/unit/test_log_level_rule.cpp b/tests/unit/test_log_level_rule.cpp new file mode 100644 index 000000000..d2895c811 --- /dev/null +++ b/tests/unit/test_log_level_rule.cpp @@ -0,0 +1,188 @@ +/* + * Unit tests for the log level rule API. + * + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* For error.h. */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 29 + +static void test_log_level_rule_error(void) +{ + int level = 9000; + struct lttng_log_level_rule *exactly = + lttng_log_level_rule_exactly_create(level); + struct lttng_log_level_rule *at_least_as_severe = + lttng_log_level_rule_at_least_as_severe_as_create( + level); + + ok(lttng_log_level_rule_get_type(NULL) == LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN, "Get type on invalid pointer"); + + ok(lttng_log_level_rule_exactly_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, NULL) returns invalid"); + ok(lttng_log_level_rule_exactly_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (valid, NULL) returns invalid"); + ok(lttng_log_level_rule_exactly_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, valid) returns invalid"); + + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, NULL) returns invalid"); + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (valid, NULL) returns invalid"); + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, valid) returns invalid"); + + lttng_log_level_rule_destroy(exactly); + lttng_log_level_rule_destroy(at_least_as_severe); +} + +static +void test_log_level_rule_serialize_deserialize(const struct lttng_log_level_rule *rule) +{ + struct lttng_log_level_rule *log_level_rule_from_buffer = NULL; + struct lttng_payload payload; + + lttng_payload_init(&payload); + + ok(lttng_log_level_rule_serialize(rule, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_log_level_rule_create_from_payload( + &view, &log_level_rule_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_log_level_rule_is_equal(rule, log_level_rule_from_buffer), "Serialized and from buffer are equal"); + + lttng_log_level_rule_destroy(log_level_rule_from_buffer); +} + +static +void test_log_level_rule_is_equal_exactly(void) +{ + int level = 9000, no_eq_level = 420; + struct lttng_log_level_rule *a, *b, *different_level, *different_type; + + /* Identical log level rules. */ + a = lttng_log_level_rule_exactly_create(level); + b = lttng_log_level_rule_exactly_create(level); + + /* Different level, same type. */ + different_level = lttng_log_level_rule_exactly_create(no_eq_level); + + /* Different type. */ + different_type = lttng_log_level_rule_at_least_as_severe_as_create(level); + + LTTNG_ASSERT(a && b && different_level && different_type); + + ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); + ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); + ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); + ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); + + lttng_log_level_rule_destroy(a); + lttng_log_level_rule_destroy(b); + lttng_log_level_rule_destroy(different_level); + lttng_log_level_rule_destroy(different_type); +} + +static +void test_log_level_rule_is_equal_at_least_as_severe_as(void) +{ + int level = 9000, no_eq_level = 420; + struct lttng_log_level_rule *a, *b, *different_level, *different_type; + + /* Identical log level rules. */ + a = lttng_log_level_rule_at_least_as_severe_as_create(level); + b = lttng_log_level_rule_at_least_as_severe_as_create(level); + + /* Different level, same type. */ + different_level = lttng_log_level_rule_at_least_as_severe_as_create(no_eq_level); + + /* Different type. */ + different_type = lttng_log_level_rule_exactly_create(level); + + LTTNG_ASSERT(a && b && different_level && different_type); + + ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); + ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); + ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); + ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); + + lttng_log_level_rule_destroy(a); + lttng_log_level_rule_destroy(b); + lttng_log_level_rule_destroy(different_level); + lttng_log_level_rule_destroy(different_type); +} + +static void test_log_level_rule_exactly(void) +{ + int level = 9000; + int _level; + struct lttng_log_level_rule *exactly = NULL; + enum lttng_log_level_rule_status status; + + exactly = lttng_log_level_rule_exactly_create(level); + + ok(exactly, "Log level exactly allocated"); + ok(lttng_log_level_rule_get_type(exactly) == + LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY, + "Log level rule exactly type"); + + status = lttng_log_level_rule_exactly_get_level(exactly, &_level); + ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); + ok(_level == level, "Level property is valid"); + + test_log_level_rule_is_equal_exactly(); + test_log_level_rule_serialize_deserialize(exactly); + lttng_log_level_rule_destroy(exactly); +} + +static void test_log_level_rule_at_least_as_severe_as(void) +{ + int level = 9000; + int _level; + struct lttng_log_level_rule *at_least_as_severe_as = NULL; + enum lttng_log_level_rule_status status; + + at_least_as_severe_as = lttng_log_level_rule_at_least_as_severe_as_create(level); + + ok(at_least_as_severe_as, "Log level at_least_as_severe_as allocated"); + ok(lttng_log_level_rule_get_type(at_least_as_severe_as) == + LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS, + "Log level rule at_least_as_severe_as type"); + + status = lttng_log_level_rule_at_least_as_severe_as_get_level(at_least_as_severe_as, &_level); + ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); + ok(_level == level, "Level property is valid"); + + test_log_level_rule_is_equal_at_least_as_severe_as(); + test_log_level_rule_serialize_deserialize(at_least_as_severe_as); + lttng_log_level_rule_destroy(at_least_as_severe_as); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_log_level_rule_exactly(); + test_log_level_rule_at_least_as_severe_as(); + test_log_level_rule_error(); + return exit_status(); +} diff --git a/tests/unit/test_notification.c b/tests/unit/test_notification.c deleted file mode 100644 index a44c8ef67..000000000 --- a/tests/unit/test_notification.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * test_notification.c - * - * Unit tests for the notification API. - * - * Copyright (C) 2017 Jonathan Rajotte - * - * SPDX-License-Identifier: MIT - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 180 - -static void test_condition_buffer_usage( - struct lttng_condition *buffer_usage_condition) -{ - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - const char *session_name = NULL; - const char *channel_name = NULL; - enum lttng_domain_type domain_type; - /* Start at a non zero value to validate initialization */ - double threshold_ratio; - uint64_t threshold_bytes; - - LTTNG_ASSERT(buffer_usage_condition); - - diag("Validating initialization"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); - - status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold byte is unset"); - - status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); - ok(!session_name, "Session name is null"); - - status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); - ok(!session_name, "Channel name is null"); - - status = lttng_condition_buffer_usage_get_domain_type(buffer_usage_condition, &domain_type); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Domain name is unset"); - - diag("Testing session name set/get"); - status = lttng_condition_buffer_usage_set_session_name(NULL, "Test"); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null condition on set session name"); - status = lttng_condition_buffer_usage_get_session_name(NULL, &session_name); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get session name with null condition"); - ok(!session_name, "Session name is null"); - status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); - ok(!session_name, "Session name is null"); - - status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, NULL); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null session name"); - status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); - ok(!session_name, "Session name is null"); - - status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, ""); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set empty session name"); - status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); - ok(!session_name, "Session name is null"); - - status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, "session420"); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set session name session420"); - status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); - ok(status == LTTNG_CONDITION_STATUS_OK, "Session name is set"); - ok(session_name, "Session name has a value"); - ok(strcmp("session420", session_name) == 0, "Session name is %s", "session420"); - - /* - * Test second set on session_name. Test invalid set and validate that - * the value is still the previous good one. - */ - - status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, ""); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set session name to empty"); - status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); - ok(status == LTTNG_CONDITION_STATUS_OK, "Session name is still set"); - ok(session_name, "Session name has a value"); - ok(strcmp("session420", session_name) == 0, "Session is still name is %s", "session420"); - - diag("Testing channel name set/get"); - status = lttng_condition_buffer_usage_set_channel_name(NULL, "Test"); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null condition on set channel name"); - status = lttng_condition_buffer_usage_get_channel_name(NULL, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get channel name with null condition"); - status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); - ok(!channel_name, "Channel name is null"); - - status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, NULL); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null channel name"); - status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); - ok(!channel_name, "Channel name is null"); - - status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, ""); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set empty channel name"); - status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); - ok(!channel_name, "Channel name is null"); - - status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, "channel420"); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set channel name channel420"); - status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_OK, "Channel name is set"); - ok(channel_name, "Channel name has a value"); - ok(strcmp("channel420", channel_name) == 0, "Channel name is %s", "channel420"); - - /* - * Test second set on channel_name. Test invalid set and validate that - * the value is still the previous good one. - */ - - status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, ""); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set channel name to empty"); - status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); - ok(status == LTTNG_CONDITION_STATUS_OK, "Channel name is still set"); - ok(channel_name, "Channel name has a value"); - ok(strcmp("channel420", channel_name) == 0, "Channel is still name is %s", "channel420"); - - diag("Testing threshold ratio set/get"); - status = lttng_condition_buffer_usage_set_threshold_ratio(NULL, 0.420); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set threshold ratio with null condition"); - status = lttng_condition_buffer_usage_get_threshold_ratio(NULL, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get threshold ratio with null condition"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); - - status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, -100.0); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set threshold ratio < 0"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); - - status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 200.0); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set Threshold ratio > 1"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); - - status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 1.0); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 1.0"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); - ok(threshold_ratio == 1.0, "Threshold ratio is 1.0"); - - status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 0.0); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 0.0"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); - ok(threshold_ratio == 0.0, "Threshold ratio is 0.0"); - - status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 0.420); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 0.420"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); - ok(threshold_ratio == 0.420, "Threshold ratio is 0.420"); - - diag("Testing threshold bytes set/get"); - status = lttng_condition_buffer_usage_set_threshold(NULL, 100000); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set threshold with null condition"); - status = lttng_condition_buffer_usage_get_threshold(NULL, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get threshold value with null condition "); - status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold is unset"); - - status = lttng_condition_buffer_usage_set_threshold(buffer_usage_condition, 100000); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold > 0"); - status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold is set"); - ok(threshold_bytes == 100000, "Threshold is %" PRIu64 , 100000); - - status = lttng_condition_buffer_usage_set_threshold(buffer_usage_condition, UINT64_MAX); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold UINT64_MAX"); - status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold is set"); - ok(threshold_bytes == UINT64_MAX, "Threshold is UINT64_MAX"); - - status = lttng_condition_buffer_usage_set_threshold(buffer_usage_condition, 0); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold == 0"); - status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold is set"); - ok(threshold_bytes == 0, "Threshold is %d", 0); - - /* - * Test value of threshold ration, since we overwrote it with a byte - * threshold. Make sure it gets squashed. - */ - diag("Testing interaction between byte and ratio thresholds"); - - threshold_ratio = -1.0; - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); - ok(threshold_ratio == -1.0, "Threshold ratio is untouched"); - - /* Set a ratio to validate that the byte threshold is now unset */ - status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 0.420); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 0.420"); - status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); - ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); - ok(threshold_ratio == 0.420, "Threshold ratio is 0.420"); - - threshold_bytes = 420; - status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold is unset"); - ok(threshold_bytes == 420, "Threshold is untouched"); - - diag("Testing domain type set/get"); - status = lttng_condition_buffer_usage_set_domain_type(NULL, LTTNG_DOMAIN_UST); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set domain type with null condition"); - status = lttng_condition_buffer_usage_get_domain_type(NULL, &domain_type); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get domain type with null condition"); - - status = lttng_condition_buffer_usage_set_domain_type(buffer_usage_condition, LTTNG_DOMAIN_NONE); - ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set domain type as LTTNG_DOMAIN_NONE"); - status = lttng_condition_buffer_usage_get_domain_type(buffer_usage_condition, &domain_type); - ok(status == LTTNG_CONDITION_STATUS_UNSET, "Domain type is unset"); - - status = lttng_condition_buffer_usage_set_domain_type(buffer_usage_condition, LTTNG_DOMAIN_UST); - ok(status == LTTNG_CONDITION_STATUS_OK, "Set domain type as LTTNG_DOMAIN_UST"); - status = lttng_condition_buffer_usage_get_domain_type(buffer_usage_condition, &domain_type); - ok(status == LTTNG_CONDITION_STATUS_OK, "Domain type is set"); - ok(domain_type == LTTNG_DOMAIN_UST, "Domain type is LTTNG_DOMAIN_UST"); -} - -static void test_condition_buffer_usage_low(void) -{ - struct lttng_condition *buffer_usage_low = NULL; - - diag("Testing lttng_condition_buffer_usage_low_create"); - buffer_usage_low = lttng_condition_buffer_usage_low_create(); - ok(buffer_usage_low, "Condition allocated"); - - ok(lttng_condition_get_type(buffer_usage_low) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, "Condition is of type \"low buffer usage\""); - - test_condition_buffer_usage(buffer_usage_low); - - lttng_condition_destroy(buffer_usage_low); -} - -static void test_condition_buffer_usage_high(void) -{ - struct lttng_condition *buffer_usage_high = NULL; - - diag("Testing lttng_condition_buffer_usage_high_create"); - buffer_usage_high = lttng_condition_buffer_usage_high_create(); - ok(buffer_usage_high, "High buffer usage condition allocated"); - - ok(lttng_condition_get_type(buffer_usage_high) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, "Condition is of type \"high buffer usage\""); - - test_condition_buffer_usage(buffer_usage_high); - - lttng_condition_destroy(buffer_usage_high); -} - -static void test_trigger(void) -{ - struct lttng_action *notify_action = NULL; - struct lttng_condition *buffer_usage_high = NULL; - struct lttng_trigger *trigger = NULL; - - notify_action = lttng_action_notify_create(); - buffer_usage_high = lttng_condition_buffer_usage_high_create(); - - trigger = lttng_trigger_create(NULL, NULL); - ok(!trigger, "lttng_trigger_create(NULL, NULL) returns null"); - trigger = lttng_trigger_create(buffer_usage_high, NULL); - ok(!trigger, "lttng_trigger_create(NON-NULL, NULL) returns null"); - trigger = lttng_trigger_create(NULL, notify_action); - ok(!trigger, "lttng_trigger_create(NULL, NON-NULL) returns null"); - - trigger = lttng_trigger_create(buffer_usage_high, notify_action); - ok(trigger, "lttng_trigger_create(NON-NULL, NON-NULL) returns an object"); - - lttng_action_destroy(notify_action); - lttng_condition_destroy(buffer_usage_high); - lttng_trigger_destroy(trigger); -} - - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_condition_buffer_usage_low(); - test_condition_buffer_usage_high(); - test_trigger(); - return exit_status(); -} diff --git a/tests/unit/test_notification.cpp b/tests/unit/test_notification.cpp new file mode 100644 index 000000000..a44c8ef67 --- /dev/null +++ b/tests/unit/test_notification.cpp @@ -0,0 +1,313 @@ +/* + * test_notification.c + * + * Unit tests for the notification API. + * + * Copyright (C) 2017 Jonathan Rajotte + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 180 + +static void test_condition_buffer_usage( + struct lttng_condition *buffer_usage_condition) +{ + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + const char *session_name = NULL; + const char *channel_name = NULL; + enum lttng_domain_type domain_type; + /* Start at a non zero value to validate initialization */ + double threshold_ratio; + uint64_t threshold_bytes; + + LTTNG_ASSERT(buffer_usage_condition); + + diag("Validating initialization"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); + + status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold byte is unset"); + + status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); + ok(!session_name, "Session name is null"); + + status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); + ok(!session_name, "Channel name is null"); + + status = lttng_condition_buffer_usage_get_domain_type(buffer_usage_condition, &domain_type); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Domain name is unset"); + + diag("Testing session name set/get"); + status = lttng_condition_buffer_usage_set_session_name(NULL, "Test"); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null condition on set session name"); + status = lttng_condition_buffer_usage_get_session_name(NULL, &session_name); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get session name with null condition"); + ok(!session_name, "Session name is null"); + status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); + ok(!session_name, "Session name is null"); + + status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, NULL); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null session name"); + status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); + ok(!session_name, "Session name is null"); + + status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, ""); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set empty session name"); + status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Session name is unset"); + ok(!session_name, "Session name is null"); + + status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, "session420"); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set session name session420"); + status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); + ok(status == LTTNG_CONDITION_STATUS_OK, "Session name is set"); + ok(session_name, "Session name has a value"); + ok(strcmp("session420", session_name) == 0, "Session name is %s", "session420"); + + /* + * Test second set on session_name. Test invalid set and validate that + * the value is still the previous good one. + */ + + status = lttng_condition_buffer_usage_set_session_name(buffer_usage_condition, ""); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set session name to empty"); + status = lttng_condition_buffer_usage_get_session_name(buffer_usage_condition, &session_name); + ok(status == LTTNG_CONDITION_STATUS_OK, "Session name is still set"); + ok(session_name, "Session name has a value"); + ok(strcmp("session420", session_name) == 0, "Session is still name is %s", "session420"); + + diag("Testing channel name set/get"); + status = lttng_condition_buffer_usage_set_channel_name(NULL, "Test"); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null condition on set channel name"); + status = lttng_condition_buffer_usage_get_channel_name(NULL, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get channel name with null condition"); + status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); + ok(!channel_name, "Channel name is null"); + + status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, NULL); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set null channel name"); + status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); + ok(!channel_name, "Channel name is null"); + + status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, ""); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set empty channel name"); + status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Channel name is unset"); + ok(!channel_name, "Channel name is null"); + + status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, "channel420"); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set channel name channel420"); + status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_OK, "Channel name is set"); + ok(channel_name, "Channel name has a value"); + ok(strcmp("channel420", channel_name) == 0, "Channel name is %s", "channel420"); + + /* + * Test second set on channel_name. Test invalid set and validate that + * the value is still the previous good one. + */ + + status = lttng_condition_buffer_usage_set_channel_name(buffer_usage_condition, ""); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set channel name to empty"); + status = lttng_condition_buffer_usage_get_channel_name(buffer_usage_condition, &channel_name); + ok(status == LTTNG_CONDITION_STATUS_OK, "Channel name is still set"); + ok(channel_name, "Channel name has a value"); + ok(strcmp("channel420", channel_name) == 0, "Channel is still name is %s", "channel420"); + + diag("Testing threshold ratio set/get"); + status = lttng_condition_buffer_usage_set_threshold_ratio(NULL, 0.420); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set threshold ratio with null condition"); + status = lttng_condition_buffer_usage_get_threshold_ratio(NULL, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get threshold ratio with null condition"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); + + status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, -100.0); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set threshold ratio < 0"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); + + status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 200.0); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set Threshold ratio > 1"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); + + status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 1.0); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 1.0"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); + ok(threshold_ratio == 1.0, "Threshold ratio is 1.0"); + + status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 0.0); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 0.0"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); + ok(threshold_ratio == 0.0, "Threshold ratio is 0.0"); + + status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 0.420); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 0.420"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); + ok(threshold_ratio == 0.420, "Threshold ratio is 0.420"); + + diag("Testing threshold bytes set/get"); + status = lttng_condition_buffer_usage_set_threshold(NULL, 100000); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set threshold with null condition"); + status = lttng_condition_buffer_usage_get_threshold(NULL, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get threshold value with null condition "); + status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold is unset"); + + status = lttng_condition_buffer_usage_set_threshold(buffer_usage_condition, 100000); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold > 0"); + status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold is set"); + ok(threshold_bytes == 100000, "Threshold is %" PRIu64 , 100000); + + status = lttng_condition_buffer_usage_set_threshold(buffer_usage_condition, UINT64_MAX); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold UINT64_MAX"); + status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold is set"); + ok(threshold_bytes == UINT64_MAX, "Threshold is UINT64_MAX"); + + status = lttng_condition_buffer_usage_set_threshold(buffer_usage_condition, 0); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold == 0"); + status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold is set"); + ok(threshold_bytes == 0, "Threshold is %d", 0); + + /* + * Test value of threshold ration, since we overwrote it with a byte + * threshold. Make sure it gets squashed. + */ + diag("Testing interaction between byte and ratio thresholds"); + + threshold_ratio = -1.0; + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold ratio is unset"); + ok(threshold_ratio == -1.0, "Threshold ratio is untouched"); + + /* Set a ratio to validate that the byte threshold is now unset */ + status = lttng_condition_buffer_usage_set_threshold_ratio(buffer_usage_condition, 0.420); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set threshold ratio == 0.420"); + status = lttng_condition_buffer_usage_get_threshold_ratio(buffer_usage_condition, &threshold_ratio); + ok(status == LTTNG_CONDITION_STATUS_OK, "Threshold ratio is set"); + ok(threshold_ratio == 0.420, "Threshold ratio is 0.420"); + + threshold_bytes = 420; + status = lttng_condition_buffer_usage_get_threshold(buffer_usage_condition, &threshold_bytes); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Threshold is unset"); + ok(threshold_bytes == 420, "Threshold is untouched"); + + diag("Testing domain type set/get"); + status = lttng_condition_buffer_usage_set_domain_type(NULL, LTTNG_DOMAIN_UST); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set domain type with null condition"); + status = lttng_condition_buffer_usage_get_domain_type(NULL, &domain_type); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Get domain type with null condition"); + + status = lttng_condition_buffer_usage_set_domain_type(buffer_usage_condition, LTTNG_DOMAIN_NONE); + ok(status == LTTNG_CONDITION_STATUS_INVALID, "Set domain type as LTTNG_DOMAIN_NONE"); + status = lttng_condition_buffer_usage_get_domain_type(buffer_usage_condition, &domain_type); + ok(status == LTTNG_CONDITION_STATUS_UNSET, "Domain type is unset"); + + status = lttng_condition_buffer_usage_set_domain_type(buffer_usage_condition, LTTNG_DOMAIN_UST); + ok(status == LTTNG_CONDITION_STATUS_OK, "Set domain type as LTTNG_DOMAIN_UST"); + status = lttng_condition_buffer_usage_get_domain_type(buffer_usage_condition, &domain_type); + ok(status == LTTNG_CONDITION_STATUS_OK, "Domain type is set"); + ok(domain_type == LTTNG_DOMAIN_UST, "Domain type is LTTNG_DOMAIN_UST"); +} + +static void test_condition_buffer_usage_low(void) +{ + struct lttng_condition *buffer_usage_low = NULL; + + diag("Testing lttng_condition_buffer_usage_low_create"); + buffer_usage_low = lttng_condition_buffer_usage_low_create(); + ok(buffer_usage_low, "Condition allocated"); + + ok(lttng_condition_get_type(buffer_usage_low) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, "Condition is of type \"low buffer usage\""); + + test_condition_buffer_usage(buffer_usage_low); + + lttng_condition_destroy(buffer_usage_low); +} + +static void test_condition_buffer_usage_high(void) +{ + struct lttng_condition *buffer_usage_high = NULL; + + diag("Testing lttng_condition_buffer_usage_high_create"); + buffer_usage_high = lttng_condition_buffer_usage_high_create(); + ok(buffer_usage_high, "High buffer usage condition allocated"); + + ok(lttng_condition_get_type(buffer_usage_high) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, "Condition is of type \"high buffer usage\""); + + test_condition_buffer_usage(buffer_usage_high); + + lttng_condition_destroy(buffer_usage_high); +} + +static void test_trigger(void) +{ + struct lttng_action *notify_action = NULL; + struct lttng_condition *buffer_usage_high = NULL; + struct lttng_trigger *trigger = NULL; + + notify_action = lttng_action_notify_create(); + buffer_usage_high = lttng_condition_buffer_usage_high_create(); + + trigger = lttng_trigger_create(NULL, NULL); + ok(!trigger, "lttng_trigger_create(NULL, NULL) returns null"); + trigger = lttng_trigger_create(buffer_usage_high, NULL); + ok(!trigger, "lttng_trigger_create(NON-NULL, NULL) returns null"); + trigger = lttng_trigger_create(NULL, notify_action); + ok(!trigger, "lttng_trigger_create(NULL, NON-NULL) returns null"); + + trigger = lttng_trigger_create(buffer_usage_high, notify_action); + ok(trigger, "lttng_trigger_create(NON-NULL, NON-NULL) returns an object"); + + lttng_action_destroy(notify_action); + lttng_condition_destroy(buffer_usage_high); + lttng_trigger_destroy(trigger); +} + + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_condition_buffer_usage_low(); + test_condition_buffer_usage_high(); + test_trigger(); + return exit_status(); +} diff --git a/tests/unit/test_payload.c b/tests/unit/test_payload.c deleted file mode 100644 index 1c79b8931..000000000 --- a/tests/unit/test_payload.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include - -#include -#include -#include -#include - -static const int TEST_COUNT = 5; - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -static void test_fd_push_pop_order(void) -{ - int ret, i; - struct lttng_payload payload; - int fds[3]; - - lttng_payload_init(&payload); - - diag("Validating fd push/pop order"); - for (i = 0; i < 3; i++) { - int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); - struct fd_handle *handle; - - LTTNG_ASSERT(fd >= 0); - fds[i] = fd; - - handle = fd_handle_create(fd); - LTTNG_ASSERT(handle); - - ret = lttng_payload_push_fd_handle(&payload, handle); - fd_handle_put(handle); - if (ret) { - break; - } - } - - ok(ret == 0, "Added three file descriptors to an lttng_payload"); - - { - bool fail_pop = false; - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - for (i = 0; i < 3; i++) { - struct fd_handle *handle = - lttng_payload_view_pop_fd_handle(&view); - - fail_pop |= fd_handle_get_fd(handle) != fds[i]; - fd_handle_put(handle); - } - - ok(!fail_pop, "File descriptors are popped from a payload view in the order of insertion"); - } - - lttng_payload_reset(&payload); -} - -static void test_fd_push_pop_imbalance(void) -{ - int ret, i; - struct lttng_payload payload; - const char * const test_description = "Error reported when popping more file descriptors than were pushed"; - - lttng_payload_init(&payload); - - diag("Validating fd pop imbalance"); - for (i = 0; i < 10; i++) { - struct fd_handle *handle; - int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); - - LTTNG_ASSERT(fd >= 0); - - handle = fd_handle_create(fd); - LTTNG_ASSERT(handle); - - ret = lttng_payload_push_fd_handle(&payload, handle); - fd_handle_put(handle); - if (ret) { - break; - } - } - - { - struct fd_handle *handle; - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - for (i = 0; i < 10; i++) { - handle = lttng_payload_view_pop_fd_handle(&view); - fd_handle_put(handle); - if (!handle) { - goto fail; - } - } - - handle = lttng_payload_view_pop_fd_handle(&view); - ok(!handle, test_description); - fd_handle_put(handle); - } - - lttng_payload_reset(&payload); - return; -fail: - fail(test_description); - lttng_payload_reset(&payload); -} - -static void test_fd_pop_fd_root_views(void) -{ - int ret, i; - int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); - struct fd_handle *handle; - struct lttng_payload payload; - const char * const test_description = "Same file descriptor returned when popping from different top-level views"; - - LTTNG_ASSERT(fd >= 0); - handle = fd_handle_create(fd); - LTTNG_ASSERT(handle); - - lttng_payload_init(&payload); - - diag("Validating root view fd pop behaviour"); - ret = lttng_payload_push_fd_handle(&payload, handle); - if (ret) { - goto fail; - } - - for (i = 0; i < 5; i++) { - int view_fd; - struct fd_handle *view_handle; - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - view_handle = lttng_payload_view_pop_fd_handle(&view); - if (!view_handle) { - goto fail; - } - - view_fd = fd_handle_get_fd(view_handle); - fd_handle_put(view_handle); - if (view_fd != fd || view_handle != handle) { - goto fail; - } - } - - lttng_payload_reset(&payload); - pass(test_description); - fd_handle_put(handle); - return; -fail: - lttng_payload_reset(&payload); - fail(test_description); - fd_handle_put(handle); -} - -static void test_fd_pop_fd_descendant_views(void) -{ - int ret; - const int fd1 = 42, fd2 = 1837; - struct fd_handle *handle1 = fd_handle_create(fd1); - struct fd_handle *handle2 = fd_handle_create(fd2); - struct fd_handle *view_handle1 = NULL, *view_handle2 = NULL; - struct lttng_payload payload; - const char * const test_description = "Different file descriptors returned when popping from descendant views"; - - lttng_payload_init(&payload); - LTTNG_ASSERT(handle1); - LTTNG_ASSERT(handle2); - - diag("Validating descendant view fd pop behaviour"); - ret = lttng_payload_push_fd_handle(&payload, handle1); - if (ret) { - goto fail; - } - - ret = lttng_payload_push_fd_handle(&payload, handle2); - if (ret) { - goto fail; - } - - { - struct lttng_payload_view view1 = - lttng_payload_view_from_payload( - &payload, 0, -1); - struct lttng_payload_view view2 = - lttng_payload_view_from_view( - &view1, 0, -1); - - view_handle1 = lttng_payload_view_pop_fd_handle(&view1); - if (!view_handle1 || fd_handle_get_fd(view_handle1) != fd1) { - goto fail; - } - - view_handle2 = lttng_payload_view_pop_fd_handle(&view2); - if (!view_handle2 || fd_handle_get_fd(view_handle2) != fd2) { - goto fail; - } - } - - lttng_payload_reset(&payload); - pass(test_description); - fd_handle_put(handle1); - fd_handle_put(handle2); - fd_handle_put(view_handle1); - fd_handle_put(view_handle2); - return; -fail: - lttng_payload_reset(&payload); - fail(test_description); - fd_handle_put(handle1); - fd_handle_put(handle2); - fd_handle_put(view_handle1); - fd_handle_put(view_handle2); -} - -int main(void) -{ - plan_tests(TEST_COUNT); - - test_fd_push_pop_order(); - test_fd_push_pop_imbalance(); - test_fd_pop_fd_root_views(); - test_fd_pop_fd_descendant_views(); - - return exit_status(); -} diff --git a/tests/unit/test_payload.cpp b/tests/unit/test_payload.cpp new file mode 100644 index 000000000..1c79b8931 --- /dev/null +++ b/tests/unit/test_payload.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +#include +#include +#include +#include + +static const int TEST_COUNT = 5; + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +static void test_fd_push_pop_order(void) +{ + int ret, i; + struct lttng_payload payload; + int fds[3]; + + lttng_payload_init(&payload); + + diag("Validating fd push/pop order"); + for (i = 0; i < 3; i++) { + int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); + struct fd_handle *handle; + + LTTNG_ASSERT(fd >= 0); + fds[i] = fd; + + handle = fd_handle_create(fd); + LTTNG_ASSERT(handle); + + ret = lttng_payload_push_fd_handle(&payload, handle); + fd_handle_put(handle); + if (ret) { + break; + } + } + + ok(ret == 0, "Added three file descriptors to an lttng_payload"); + + { + bool fail_pop = false; + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + for (i = 0; i < 3; i++) { + struct fd_handle *handle = + lttng_payload_view_pop_fd_handle(&view); + + fail_pop |= fd_handle_get_fd(handle) != fds[i]; + fd_handle_put(handle); + } + + ok(!fail_pop, "File descriptors are popped from a payload view in the order of insertion"); + } + + lttng_payload_reset(&payload); +} + +static void test_fd_push_pop_imbalance(void) +{ + int ret, i; + struct lttng_payload payload; + const char * const test_description = "Error reported when popping more file descriptors than were pushed"; + + lttng_payload_init(&payload); + + diag("Validating fd pop imbalance"); + for (i = 0; i < 10; i++) { + struct fd_handle *handle; + int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); + + LTTNG_ASSERT(fd >= 0); + + handle = fd_handle_create(fd); + LTTNG_ASSERT(handle); + + ret = lttng_payload_push_fd_handle(&payload, handle); + fd_handle_put(handle); + if (ret) { + break; + } + } + + { + struct fd_handle *handle; + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + for (i = 0; i < 10; i++) { + handle = lttng_payload_view_pop_fd_handle(&view); + fd_handle_put(handle); + if (!handle) { + goto fail; + } + } + + handle = lttng_payload_view_pop_fd_handle(&view); + ok(!handle, test_description); + fd_handle_put(handle); + } + + lttng_payload_reset(&payload); + return; +fail: + fail(test_description); + lttng_payload_reset(&payload); +} + +static void test_fd_pop_fd_root_views(void) +{ + int ret, i; + int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); + struct fd_handle *handle; + struct lttng_payload payload; + const char * const test_description = "Same file descriptor returned when popping from different top-level views"; + + LTTNG_ASSERT(fd >= 0); + handle = fd_handle_create(fd); + LTTNG_ASSERT(handle); + + lttng_payload_init(&payload); + + diag("Validating root view fd pop behaviour"); + ret = lttng_payload_push_fd_handle(&payload, handle); + if (ret) { + goto fail; + } + + for (i = 0; i < 5; i++) { + int view_fd; + struct fd_handle *view_handle; + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + view_handle = lttng_payload_view_pop_fd_handle(&view); + if (!view_handle) { + goto fail; + } + + view_fd = fd_handle_get_fd(view_handle); + fd_handle_put(view_handle); + if (view_fd != fd || view_handle != handle) { + goto fail; + } + } + + lttng_payload_reset(&payload); + pass(test_description); + fd_handle_put(handle); + return; +fail: + lttng_payload_reset(&payload); + fail(test_description); + fd_handle_put(handle); +} + +static void test_fd_pop_fd_descendant_views(void) +{ + int ret; + const int fd1 = 42, fd2 = 1837; + struct fd_handle *handle1 = fd_handle_create(fd1); + struct fd_handle *handle2 = fd_handle_create(fd2); + struct fd_handle *view_handle1 = NULL, *view_handle2 = NULL; + struct lttng_payload payload; + const char * const test_description = "Different file descriptors returned when popping from descendant views"; + + lttng_payload_init(&payload); + LTTNG_ASSERT(handle1); + LTTNG_ASSERT(handle2); + + diag("Validating descendant view fd pop behaviour"); + ret = lttng_payload_push_fd_handle(&payload, handle1); + if (ret) { + goto fail; + } + + ret = lttng_payload_push_fd_handle(&payload, handle2); + if (ret) { + goto fail; + } + + { + struct lttng_payload_view view1 = + lttng_payload_view_from_payload( + &payload, 0, -1); + struct lttng_payload_view view2 = + lttng_payload_view_from_view( + &view1, 0, -1); + + view_handle1 = lttng_payload_view_pop_fd_handle(&view1); + if (!view_handle1 || fd_handle_get_fd(view_handle1) != fd1) { + goto fail; + } + + view_handle2 = lttng_payload_view_pop_fd_handle(&view2); + if (!view_handle2 || fd_handle_get_fd(view_handle2) != fd2) { + goto fail; + } + } + + lttng_payload_reset(&payload); + pass(test_description); + fd_handle_put(handle1); + fd_handle_put(handle2); + fd_handle_put(view_handle1); + fd_handle_put(view_handle2); + return; +fail: + lttng_payload_reset(&payload); + fail(test_description); + fd_handle_put(handle1); + fd_handle_put(handle2); + fd_handle_put(view_handle1); + fd_handle_put(view_handle2); +} + +int main(void) +{ + plan_tests(TEST_COUNT); + + test_fd_push_pop_order(); + test_fd_push_pop_imbalance(); + test_fd_pop_fd_root_views(); + test_fd_pop_fd_descendant_views(); + + return exit_status(); +} diff --git a/tests/unit/test_rate_policy.c b/tests/unit/test_rate_policy.c deleted file mode 100644 index acfe15903..000000000 --- a/tests/unit/test_rate_policy.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Unit tests for the rate policy object API. - * - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -/* For error.h. */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -#define NUM_TESTS 42 - -static void test_rate_policy_every_n(void) -{ - enum lttng_rate_policy_status status; - struct lttng_rate_policy *policy_a = NULL; /* Interval of 100. */ - struct lttng_rate_policy *policy_b = NULL; /* Interval of 100 */ - struct lttng_rate_policy *policy_c = NULL; /* Interval of 1 */ - struct lttng_rate_policy *policy_from_buffer = NULL; - uint64_t interval_a_b = 100; - uint64_t interval_c = 1; - uint64_t interval_query = 0; - struct lttng_payload payload; - - lttng_payload_init(&payload); - - policy_a = lttng_rate_policy_every_n_create(interval_a_b); - policy_b = lttng_rate_policy_every_n_create(interval_a_b); - policy_c = lttng_rate_policy_every_n_create(interval_c); - ok(policy_a != NULL, - "Rate policy every n A created: interval: %" PRIu64, - interval_a_b); - ok(policy_b != NULL, - "Rate policy every n B created: interval: %" PRIu64, - interval_a_b); - ok(policy_c != NULL, - "Rate policy every n C created: interval: %" PRIu64, - interval_c); - - ok(LTTNG_RATE_POLICY_TYPE_EVERY_N == - lttng_rate_policy_get_type(policy_a), - "Type is LTTNG_RATE_POLICY_TYPE_EVERY_N"); - - /* Getter tests */ - status = lttng_rate_policy_every_n_get_interval(NULL, NULL); - ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, - "Get interval returns INVALID"); - - status = lttng_rate_policy_every_n_get_interval(NULL, &interval_query); - ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, - "Get interval returns INVALID"); - - status = lttng_rate_policy_every_n_get_interval(policy_a, NULL); - ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, - "Get interval returns INVALID"); - - status = lttng_rate_policy_every_n_get_interval( - policy_a, &interval_query); - ok(status == LTTNG_RATE_POLICY_STATUS_OK && - interval_query == interval_a_b, - " Getting interval A"); - - status = lttng_rate_policy_every_n_get_interval( - policy_b, &interval_query); - ok(status == LTTNG_RATE_POLICY_STATUS_OK && - interval_query == interval_a_b, - " Getting interval B"); - - status = lttng_rate_policy_every_n_get_interval( - policy_c, &interval_query); - ok(status == LTTNG_RATE_POLICY_STATUS_OK && - interval_query == interval_c, - " Getting interval C"); - - /* is_equal tests */ - /* TODO: this is the behaviour introduced by the - * lttng_condition_is_equal back in 2017 do we want to fix this and - * return true if both are NULL? - */ - ok(false == lttng_rate_policy_is_equal(NULL, NULL), - "is equal (NULL,NULL)"); - ok(false == lttng_rate_policy_is_equal(policy_a, NULL), - "is equal (object, NULL)"); - ok(false == lttng_rate_policy_is_equal(NULL, policy_a), - " is equal (NULL, object)"); - ok(true == lttng_rate_policy_is_equal(policy_a, policy_a), - "is equal (object A, object A)"); - - ok(true == lttng_rate_policy_is_equal(policy_a, policy_b), - "is equal (object A, object B"); - ok(true == lttng_rate_policy_is_equal(policy_b, policy_a), - "is equal (object B, object A"); - - ok(false == lttng_rate_policy_is_equal(policy_a, policy_c), - "is equal (object A, object C)"); - ok(false == lttng_rate_policy_is_equal(policy_c, policy_a), - "is equal (object C, object A)"); - - /* Serialization and create_from buffer. */ - ok(lttng_rate_policy_serialize(policy_a, &payload) == 0, "Serializing"); - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_rate_policy_create_from_payload( - &view, &policy_from_buffer) > 0 && - policy_from_buffer != NULL, - "Deserializing"); - } - - ok(lttng_rate_policy_is_equal(policy_a, policy_from_buffer), - "serialized and from buffer are equal"); - - lttng_rate_policy_destroy(policy_a); - lttng_rate_policy_destroy(policy_b); - lttng_rate_policy_destroy(policy_c); - lttng_payload_reset(&payload); -} - -static void test_rate_policy_once_after_n(void) -{ - enum lttng_rate_policy_status status; - struct lttng_rate_policy *policy_a = NULL; /* Threshold of 100. */ - struct lttng_rate_policy *policy_b = NULL; /* threshold of 100 */ - struct lttng_rate_policy *policy_c = NULL; /* threshold of 1 */ - struct lttng_rate_policy *policy_from_buffer = NULL; - uint64_t threshold_a_b = 100; - uint64_t threshold_c = 1; - uint64_t threshold_query = 0; - struct lttng_payload payload; - - lttng_payload_init(&payload); - - policy_a = lttng_rate_policy_once_after_n_create(threshold_a_b); - policy_b = lttng_rate_policy_once_after_n_create(threshold_a_b); - policy_c = lttng_rate_policy_once_after_n_create(threshold_c); - ok(policy_a != NULL, - "Rate policy every n A created: threshold: %" PRIu64, - threshold_a_b); - ok(policy_b != NULL, - "Rate policy every n B created: threshold: %" PRIu64, - threshold_a_b); - ok(policy_c != NULL, - "Rate policy every n C created: threshold: %" PRIu64, - threshold_c); - - ok(LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N == - lttng_rate_policy_get_type(policy_a), - "Type is LTTNG_RATE_POLICY_TYPE_once_after_n"); - - /* Getter tests */ - status = lttng_rate_policy_once_after_n_get_threshold(NULL, NULL); - ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, - "Get threshold returns INVALID"); - - status = lttng_rate_policy_once_after_n_get_threshold( - NULL, &threshold_query); - ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, - "Get threshold returns INVALID"); - - status = lttng_rate_policy_once_after_n_get_threshold(policy_a, NULL); - ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, - "Get threshold returns INVALID"); - - status = lttng_rate_policy_once_after_n_get_threshold( - policy_a, &threshold_query); - ok(status == LTTNG_RATE_POLICY_STATUS_OK && - threshold_query == threshold_a_b, - " Getting threshold A"); - - status = lttng_rate_policy_once_after_n_get_threshold( - policy_b, &threshold_query); - ok(status == LTTNG_RATE_POLICY_STATUS_OK && - threshold_query == threshold_a_b, - " Getting threshold B"); - - status = lttng_rate_policy_once_after_n_get_threshold( - policy_c, &threshold_query); - ok(status == LTTNG_RATE_POLICY_STATUS_OK && - threshold_query == threshold_c, - " Getting threshold C"); - - /* is_equal tests */ - /* TODO: this is the behaviour introduced by the - * lttng_condition_is_equal back in 2017 do we want to fix this and - * return true if both are NULL? - */ - ok(false == lttng_rate_policy_is_equal(NULL, NULL), - "is equal (NULL,NULL)"); - ok(false == lttng_rate_policy_is_equal(policy_a, NULL), - "is equal (object, NULL)"); - ok(false == lttng_rate_policy_is_equal(NULL, policy_a), - " is equal (NULL, object)"); - ok(true == lttng_rate_policy_is_equal(policy_a, policy_a), - "is equal (object A, object A)"); - - ok(true == lttng_rate_policy_is_equal(policy_a, policy_b), - "is equal (object A, object B"); - ok(true == lttng_rate_policy_is_equal(policy_b, policy_a), - "is equal (object B, object A"); - - ok(false == lttng_rate_policy_is_equal(policy_a, policy_c), - "is equal (object A, object C)"); - ok(false == lttng_rate_policy_is_equal(policy_c, policy_a), - "is equal (object C, object A)"); - - /* Serialization and create_from buffer. */ - ok(lttng_rate_policy_serialize(policy_a, &payload) == 0, "Serializing"); - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &payload, 0, -1); - - ok(lttng_rate_policy_create_from_payload( - &view, &policy_from_buffer) > 0 && - policy_from_buffer != NULL, - "Deserializing"); - } - - ok(lttng_rate_policy_is_equal(policy_a, policy_from_buffer), - "serialized and from buffer are equal"); - - lttng_rate_policy_destroy(policy_a); - lttng_rate_policy_destroy(policy_b); - lttng_rate_policy_destroy(policy_c); - lttng_payload_reset(&payload); -} - -int main(int argc, const char *argv[]) -{ - plan_tests(NUM_TESTS); - test_rate_policy_every_n(); - test_rate_policy_once_after_n(); - return exit_status(); -} diff --git a/tests/unit/test_rate_policy.cpp b/tests/unit/test_rate_policy.cpp new file mode 100644 index 000000000..acfe15903 --- /dev/null +++ b/tests/unit/test_rate_policy.cpp @@ -0,0 +1,252 @@ +/* + * Unit tests for the rate policy object API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* For error.h. */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 42 + +static void test_rate_policy_every_n(void) +{ + enum lttng_rate_policy_status status; + struct lttng_rate_policy *policy_a = NULL; /* Interval of 100. */ + struct lttng_rate_policy *policy_b = NULL; /* Interval of 100 */ + struct lttng_rate_policy *policy_c = NULL; /* Interval of 1 */ + struct lttng_rate_policy *policy_from_buffer = NULL; + uint64_t interval_a_b = 100; + uint64_t interval_c = 1; + uint64_t interval_query = 0; + struct lttng_payload payload; + + lttng_payload_init(&payload); + + policy_a = lttng_rate_policy_every_n_create(interval_a_b); + policy_b = lttng_rate_policy_every_n_create(interval_a_b); + policy_c = lttng_rate_policy_every_n_create(interval_c); + ok(policy_a != NULL, + "Rate policy every n A created: interval: %" PRIu64, + interval_a_b); + ok(policy_b != NULL, + "Rate policy every n B created: interval: %" PRIu64, + interval_a_b); + ok(policy_c != NULL, + "Rate policy every n C created: interval: %" PRIu64, + interval_c); + + ok(LTTNG_RATE_POLICY_TYPE_EVERY_N == + lttng_rate_policy_get_type(policy_a), + "Type is LTTNG_RATE_POLICY_TYPE_EVERY_N"); + + /* Getter tests */ + status = lttng_rate_policy_every_n_get_interval(NULL, NULL); + ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, + "Get interval returns INVALID"); + + status = lttng_rate_policy_every_n_get_interval(NULL, &interval_query); + ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, + "Get interval returns INVALID"); + + status = lttng_rate_policy_every_n_get_interval(policy_a, NULL); + ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, + "Get interval returns INVALID"); + + status = lttng_rate_policy_every_n_get_interval( + policy_a, &interval_query); + ok(status == LTTNG_RATE_POLICY_STATUS_OK && + interval_query == interval_a_b, + " Getting interval A"); + + status = lttng_rate_policy_every_n_get_interval( + policy_b, &interval_query); + ok(status == LTTNG_RATE_POLICY_STATUS_OK && + interval_query == interval_a_b, + " Getting interval B"); + + status = lttng_rate_policy_every_n_get_interval( + policy_c, &interval_query); + ok(status == LTTNG_RATE_POLICY_STATUS_OK && + interval_query == interval_c, + " Getting interval C"); + + /* is_equal tests */ + /* TODO: this is the behaviour introduced by the + * lttng_condition_is_equal back in 2017 do we want to fix this and + * return true if both are NULL? + */ + ok(false == lttng_rate_policy_is_equal(NULL, NULL), + "is equal (NULL,NULL)"); + ok(false == lttng_rate_policy_is_equal(policy_a, NULL), + "is equal (object, NULL)"); + ok(false == lttng_rate_policy_is_equal(NULL, policy_a), + " is equal (NULL, object)"); + ok(true == lttng_rate_policy_is_equal(policy_a, policy_a), + "is equal (object A, object A)"); + + ok(true == lttng_rate_policy_is_equal(policy_a, policy_b), + "is equal (object A, object B"); + ok(true == lttng_rate_policy_is_equal(policy_b, policy_a), + "is equal (object B, object A"); + + ok(false == lttng_rate_policy_is_equal(policy_a, policy_c), + "is equal (object A, object C)"); + ok(false == lttng_rate_policy_is_equal(policy_c, policy_a), + "is equal (object C, object A)"); + + /* Serialization and create_from buffer. */ + ok(lttng_rate_policy_serialize(policy_a, &payload) == 0, "Serializing"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_rate_policy_create_from_payload( + &view, &policy_from_buffer) > 0 && + policy_from_buffer != NULL, + "Deserializing"); + } + + ok(lttng_rate_policy_is_equal(policy_a, policy_from_buffer), + "serialized and from buffer are equal"); + + lttng_rate_policy_destroy(policy_a); + lttng_rate_policy_destroy(policy_b); + lttng_rate_policy_destroy(policy_c); + lttng_payload_reset(&payload); +} + +static void test_rate_policy_once_after_n(void) +{ + enum lttng_rate_policy_status status; + struct lttng_rate_policy *policy_a = NULL; /* Threshold of 100. */ + struct lttng_rate_policy *policy_b = NULL; /* threshold of 100 */ + struct lttng_rate_policy *policy_c = NULL; /* threshold of 1 */ + struct lttng_rate_policy *policy_from_buffer = NULL; + uint64_t threshold_a_b = 100; + uint64_t threshold_c = 1; + uint64_t threshold_query = 0; + struct lttng_payload payload; + + lttng_payload_init(&payload); + + policy_a = lttng_rate_policy_once_after_n_create(threshold_a_b); + policy_b = lttng_rate_policy_once_after_n_create(threshold_a_b); + policy_c = lttng_rate_policy_once_after_n_create(threshold_c); + ok(policy_a != NULL, + "Rate policy every n A created: threshold: %" PRIu64, + threshold_a_b); + ok(policy_b != NULL, + "Rate policy every n B created: threshold: %" PRIu64, + threshold_a_b); + ok(policy_c != NULL, + "Rate policy every n C created: threshold: %" PRIu64, + threshold_c); + + ok(LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N == + lttng_rate_policy_get_type(policy_a), + "Type is LTTNG_RATE_POLICY_TYPE_once_after_n"); + + /* Getter tests */ + status = lttng_rate_policy_once_after_n_get_threshold(NULL, NULL); + ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, + "Get threshold returns INVALID"); + + status = lttng_rate_policy_once_after_n_get_threshold( + NULL, &threshold_query); + ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, + "Get threshold returns INVALID"); + + status = lttng_rate_policy_once_after_n_get_threshold(policy_a, NULL); + ok(status == LTTNG_RATE_POLICY_STATUS_INVALID, + "Get threshold returns INVALID"); + + status = lttng_rate_policy_once_after_n_get_threshold( + policy_a, &threshold_query); + ok(status == LTTNG_RATE_POLICY_STATUS_OK && + threshold_query == threshold_a_b, + " Getting threshold A"); + + status = lttng_rate_policy_once_after_n_get_threshold( + policy_b, &threshold_query); + ok(status == LTTNG_RATE_POLICY_STATUS_OK && + threshold_query == threshold_a_b, + " Getting threshold B"); + + status = lttng_rate_policy_once_after_n_get_threshold( + policy_c, &threshold_query); + ok(status == LTTNG_RATE_POLICY_STATUS_OK && + threshold_query == threshold_c, + " Getting threshold C"); + + /* is_equal tests */ + /* TODO: this is the behaviour introduced by the + * lttng_condition_is_equal back in 2017 do we want to fix this and + * return true if both are NULL? + */ + ok(false == lttng_rate_policy_is_equal(NULL, NULL), + "is equal (NULL,NULL)"); + ok(false == lttng_rate_policy_is_equal(policy_a, NULL), + "is equal (object, NULL)"); + ok(false == lttng_rate_policy_is_equal(NULL, policy_a), + " is equal (NULL, object)"); + ok(true == lttng_rate_policy_is_equal(policy_a, policy_a), + "is equal (object A, object A)"); + + ok(true == lttng_rate_policy_is_equal(policy_a, policy_b), + "is equal (object A, object B"); + ok(true == lttng_rate_policy_is_equal(policy_b, policy_a), + "is equal (object B, object A"); + + ok(false == lttng_rate_policy_is_equal(policy_a, policy_c), + "is equal (object A, object C)"); + ok(false == lttng_rate_policy_is_equal(policy_c, policy_a), + "is equal (object C, object A)"); + + /* Serialization and create_from buffer. */ + ok(lttng_rate_policy_serialize(policy_a, &payload) == 0, "Serializing"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_rate_policy_create_from_payload( + &view, &policy_from_buffer) > 0 && + policy_from_buffer != NULL, + "Deserializing"); + } + + ok(lttng_rate_policy_is_equal(policy_a, policy_from_buffer), + "serialized and from buffer are equal"); + + lttng_rate_policy_destroy(policy_a); + lttng_rate_policy_destroy(policy_b); + lttng_rate_policy_destroy(policy_c); + lttng_payload_reset(&payload); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_rate_policy_every_n(); + test_rate_policy_once_after_n(); + return exit_status(); +} diff --git a/tests/unit/test_string_utils.c b/tests/unit/test_string_utils.c deleted file mode 100644 index d2b0d905d..000000000 --- a/tests/unit/test_string_utils.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2017 Philippe Proulx - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include - -/* Number of TAP tests in this file */ -#define NUM_TESTS 69 - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -static void test_one_split(const char *input, char delim, int escape_delim, - ...) -{ - va_list vl; - bool all_ok = true; - struct lttng_dynamic_pointer_array strings; - int split_ret; - size_t i, string_count; - - split_ret = strutils_split(input, delim, escape_delim, &strings); - LTTNG_ASSERT(split_ret == 0); - va_start(vl, escape_delim); - - string_count = lttng_dynamic_pointer_array_get_count(&strings); - - for (i = 0; i < string_count; i++) { - const char *expected_substring = va_arg(vl, const char *); - const char *substring = - lttng_dynamic_pointer_array_get_pointer( - &strings, i); - - diag(" got `%s`, expecting `%s`", substring, expected_substring); - - if (!expected_substring) { - all_ok = false; - break; - } - - if (strcmp(substring, expected_substring) != 0) { - all_ok = false; - break; - } - } - - lttng_dynamic_pointer_array_reset(&strings); - va_end(vl); - ok(all_ok, "strutils_split() produces the expected substrings: `%s` (delim. `%c`, escape `%d`)", - input, delim, escape_delim); -} - -static void test_split(void) -{ - test_one_split("a/b/c/d/e", '/', false, "a", "b", "c", "d", "e", NULL); - test_one_split("a/b//d/e", '/', false, "a", "b", "", "d", "e", NULL); - test_one_split("/b/c/d/e", '/', false, "", "b", "c", "d", "e", NULL); - test_one_split("a/b/c/d/", '/', false, "a", "b", "c", "d", "", NULL); - test_one_split("/b/c/d/", '/', false, "", "b", "c", "d", "", NULL); - test_one_split("", '/', false, "", NULL); - test_one_split("/", '/', false, "", "", NULL); - test_one_split("//", '/', false, "", "", "", NULL); - test_one_split("hello+world", '+', false, "hello", "world", NULL); - test_one_split("hello\\+world", '+', false, "hello\\", "world", NULL); - test_one_split("hello\\+world", '+', true, "hello+world", NULL); - test_one_split("hello\\++world", '+', true, "hello+", "world", NULL); - test_one_split("hello\\\\++world", '+', true, "hello\\\\", "", "world", NULL); - test_one_split("hello+world\\", '+', false, "hello", "world\\", NULL); - test_one_split("hello+world\\", '+', true, "hello", "world\\", NULL); - test_one_split("\\+", '+', false, "\\", "", NULL); - test_one_split("\\+", '+', true, "+", NULL); -} - -static void test_one_is_star_at_the_end_only_glob_pattern(const char *pattern, bool expected) -{ - ok(strutils_is_star_at_the_end_only_glob_pattern(pattern) == expected, - "strutils_is_star_at_the_end_only_glob_pattern() returns the expected result: `%s` -> %d", - pattern, expected); -} - -static void test_is_star_at_the_end_only_glob_pattern(void) -{ - test_one_is_star_at_the_end_only_glob_pattern("allo*", true); - test_one_is_star_at_the_end_only_glob_pattern("allo\\\\*", true); - test_one_is_star_at_the_end_only_glob_pattern("allo", false); - test_one_is_star_at_the_end_only_glob_pattern("al*lo", false); - test_one_is_star_at_the_end_only_glob_pattern("al\\*lo", false); - test_one_is_star_at_the_end_only_glob_pattern("*allo", false); - test_one_is_star_at_the_end_only_glob_pattern("al*lo*", false); - test_one_is_star_at_the_end_only_glob_pattern("allo**", false); - test_one_is_star_at_the_end_only_glob_pattern("allo*\\*", false); - test_one_is_star_at_the_end_only_glob_pattern("allo\\*", false); -} - -static void test_one_is_star_glob_pattern(const char *pattern, bool expected) -{ - ok(strutils_is_star_glob_pattern(pattern) == expected, - "strutils_is_star_glob_pattern() returns the expected result: `%s` -> %d", - pattern, expected); -} - -static void test_is_star_glob_pattern(void) -{ - test_one_is_star_glob_pattern("allo*", true); - test_one_is_star_glob_pattern("*allo", true); - test_one_is_star_glob_pattern("*allo*", true); - test_one_is_star_glob_pattern("*al*lo*", true); - test_one_is_star_glob_pattern("al\\**lo", true); - test_one_is_star_glob_pattern("al\\*l*o", true); - test_one_is_star_glob_pattern("all*o\\", true); - test_one_is_star_glob_pattern("*", true); - test_one_is_star_glob_pattern("\\\\*", true); - test_one_is_star_glob_pattern("allo", false); - test_one_is_star_glob_pattern("allo\\*", false); - test_one_is_star_glob_pattern("al\\*lo", false); - test_one_is_star_glob_pattern("\\*allo", false); - test_one_is_star_glob_pattern("\\*", false); - test_one_is_star_glob_pattern("allo\\", false); -} - -static void test_one_normalize_star_glob_pattern(const char *pattern, - const char *expected) -{ - char *rw_pattern = strdup(pattern); - - LTTNG_ASSERT(rw_pattern); - strutils_normalize_star_glob_pattern(rw_pattern); - ok(strcmp(rw_pattern, expected) == 0, - "strutils_normalize_star_glob_pattern() produces the expected result: `%s` -> `%s`", - pattern, expected); - free(rw_pattern); -} - -static void test_normalize_star_glob_pattern(void) -{ - test_one_normalize_star_glob_pattern("salut", "salut"); - test_one_normalize_star_glob_pattern("sal*ut", "sal*ut"); - test_one_normalize_star_glob_pattern("sal**ut", "sal*ut"); - test_one_normalize_star_glob_pattern("sal***ut", "sal*ut"); - test_one_normalize_star_glob_pattern("*salut", "*salut"); - test_one_normalize_star_glob_pattern("**salut", "*salut"); - test_one_normalize_star_glob_pattern("***salut", "*salut"); - test_one_normalize_star_glob_pattern("salut*", "salut*"); - test_one_normalize_star_glob_pattern("salut**", "salut*"); - test_one_normalize_star_glob_pattern("salut***", "salut*"); - test_one_normalize_star_glob_pattern("sa\\*lut", "sa\\*lut"); - test_one_normalize_star_glob_pattern("sa\\**lut", "sa\\**lut"); - test_one_normalize_star_glob_pattern("sa*\\**lut", "sa*\\**lut"); - test_one_normalize_star_glob_pattern("sa*\\***lut", "sa*\\**lut"); - test_one_normalize_star_glob_pattern("\\*salu**t", "\\*salu*t"); - test_one_normalize_star_glob_pattern("\\*salut**", "\\*salut*"); - test_one_normalize_star_glob_pattern("\\*salut**\\*", "\\*salut*\\*"); - test_one_normalize_star_glob_pattern("\\*salut", "\\*salut"); - test_one_normalize_star_glob_pattern("\\***salut", "\\**salut"); - test_one_normalize_star_glob_pattern("salut\\", "salut\\"); - test_one_normalize_star_glob_pattern("salut\\**", "salut\\**"); - test_one_normalize_star_glob_pattern("salut\\\\*", "salut\\\\*"); - test_one_normalize_star_glob_pattern("salut\\\\***", "salut\\\\*"); - test_one_normalize_star_glob_pattern("*", "*"); - test_one_normalize_star_glob_pattern("**", "*"); - test_one_normalize_star_glob_pattern("***", "*"); - test_one_normalize_star_glob_pattern("**\\***", "*\\**"); -} - -int main(int argc, char **argv) -{ - plan_tests(NUM_TESTS); - diag("String utils unit tests"); - test_normalize_star_glob_pattern(); - test_is_star_glob_pattern(); - test_is_star_at_the_end_only_glob_pattern(); - test_split(); - - return exit_status(); -} diff --git a/tests/unit/test_string_utils.cpp b/tests/unit/test_string_utils.cpp new file mode 100644 index 000000000..e20f88f7a --- /dev/null +++ b/tests/unit/test_string_utils.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2017 Philippe Proulx + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include + +/* Number of TAP tests in this file */ +#define NUM_TESTS 69 + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +static void test_one_split(const char *input, char delim, int escape_delim, + ...) +{ + va_list vl; + bool all_ok = true; + struct lttng_dynamic_pointer_array strings; + int split_ret; + size_t i, string_count; + + split_ret = strutils_split(input, delim, escape_delim, &strings); + LTTNG_ASSERT(split_ret == 0); + va_start(vl, escape_delim); + + string_count = lttng_dynamic_pointer_array_get_count(&strings); + + for (i = 0; i < string_count; i++) { + const char *expected_substring = va_arg(vl, const char *); + const char *substring = + (const char *) lttng_dynamic_pointer_array_get_pointer( + &strings, i); + + diag(" got `%s`, expecting `%s`", substring, expected_substring); + + if (!expected_substring) { + all_ok = false; + break; + } + + if (strcmp(substring, expected_substring) != 0) { + all_ok = false; + break; + } + } + + lttng_dynamic_pointer_array_reset(&strings); + va_end(vl); + ok(all_ok, "strutils_split() produces the expected substrings: `%s` (delim. `%c`, escape `%d`)", + input, delim, escape_delim); +} + +static void test_split(void) +{ + test_one_split("a/b/c/d/e", '/', false, "a", "b", "c", "d", "e", NULL); + test_one_split("a/b//d/e", '/', false, "a", "b", "", "d", "e", NULL); + test_one_split("/b/c/d/e", '/', false, "", "b", "c", "d", "e", NULL); + test_one_split("a/b/c/d/", '/', false, "a", "b", "c", "d", "", NULL); + test_one_split("/b/c/d/", '/', false, "", "b", "c", "d", "", NULL); + test_one_split("", '/', false, "", NULL); + test_one_split("/", '/', false, "", "", NULL); + test_one_split("//", '/', false, "", "", "", NULL); + test_one_split("hello+world", '+', false, "hello", "world", NULL); + test_one_split("hello\\+world", '+', false, "hello\\", "world", NULL); + test_one_split("hello\\+world", '+', true, "hello+world", NULL); + test_one_split("hello\\++world", '+', true, "hello+", "world", NULL); + test_one_split("hello\\\\++world", '+', true, "hello\\\\", "", "world", NULL); + test_one_split("hello+world\\", '+', false, "hello", "world\\", NULL); + test_one_split("hello+world\\", '+', true, "hello", "world\\", NULL); + test_one_split("\\+", '+', false, "\\", "", NULL); + test_one_split("\\+", '+', true, "+", NULL); +} + +static void test_one_is_star_at_the_end_only_glob_pattern(const char *pattern, bool expected) +{ + ok(strutils_is_star_at_the_end_only_glob_pattern(pattern) == expected, + "strutils_is_star_at_the_end_only_glob_pattern() returns the expected result: `%s` -> %d", + pattern, expected); +} + +static void test_is_star_at_the_end_only_glob_pattern(void) +{ + test_one_is_star_at_the_end_only_glob_pattern("allo*", true); + test_one_is_star_at_the_end_only_glob_pattern("allo\\\\*", true); + test_one_is_star_at_the_end_only_glob_pattern("allo", false); + test_one_is_star_at_the_end_only_glob_pattern("al*lo", false); + test_one_is_star_at_the_end_only_glob_pattern("al\\*lo", false); + test_one_is_star_at_the_end_only_glob_pattern("*allo", false); + test_one_is_star_at_the_end_only_glob_pattern("al*lo*", false); + test_one_is_star_at_the_end_only_glob_pattern("allo**", false); + test_one_is_star_at_the_end_only_glob_pattern("allo*\\*", false); + test_one_is_star_at_the_end_only_glob_pattern("allo\\*", false); +} + +static void test_one_is_star_glob_pattern(const char *pattern, bool expected) +{ + ok(strutils_is_star_glob_pattern(pattern) == expected, + "strutils_is_star_glob_pattern() returns the expected result: `%s` -> %d", + pattern, expected); +} + +static void test_is_star_glob_pattern(void) +{ + test_one_is_star_glob_pattern("allo*", true); + test_one_is_star_glob_pattern("*allo", true); + test_one_is_star_glob_pattern("*allo*", true); + test_one_is_star_glob_pattern("*al*lo*", true); + test_one_is_star_glob_pattern("al\\**lo", true); + test_one_is_star_glob_pattern("al\\*l*o", true); + test_one_is_star_glob_pattern("all*o\\", true); + test_one_is_star_glob_pattern("*", true); + test_one_is_star_glob_pattern("\\\\*", true); + test_one_is_star_glob_pattern("allo", false); + test_one_is_star_glob_pattern("allo\\*", false); + test_one_is_star_glob_pattern("al\\*lo", false); + test_one_is_star_glob_pattern("\\*allo", false); + test_one_is_star_glob_pattern("\\*", false); + test_one_is_star_glob_pattern("allo\\", false); +} + +static void test_one_normalize_star_glob_pattern(const char *pattern, + const char *expected) +{ + char *rw_pattern = strdup(pattern); + + LTTNG_ASSERT(rw_pattern); + strutils_normalize_star_glob_pattern(rw_pattern); + ok(strcmp(rw_pattern, expected) == 0, + "strutils_normalize_star_glob_pattern() produces the expected result: `%s` -> `%s`", + pattern, expected); + free(rw_pattern); +} + +static void test_normalize_star_glob_pattern(void) +{ + test_one_normalize_star_glob_pattern("salut", "salut"); + test_one_normalize_star_glob_pattern("sal*ut", "sal*ut"); + test_one_normalize_star_glob_pattern("sal**ut", "sal*ut"); + test_one_normalize_star_glob_pattern("sal***ut", "sal*ut"); + test_one_normalize_star_glob_pattern("*salut", "*salut"); + test_one_normalize_star_glob_pattern("**salut", "*salut"); + test_one_normalize_star_glob_pattern("***salut", "*salut"); + test_one_normalize_star_glob_pattern("salut*", "salut*"); + test_one_normalize_star_glob_pattern("salut**", "salut*"); + test_one_normalize_star_glob_pattern("salut***", "salut*"); + test_one_normalize_star_glob_pattern("sa\\*lut", "sa\\*lut"); + test_one_normalize_star_glob_pattern("sa\\**lut", "sa\\**lut"); + test_one_normalize_star_glob_pattern("sa*\\**lut", "sa*\\**lut"); + test_one_normalize_star_glob_pattern("sa*\\***lut", "sa*\\**lut"); + test_one_normalize_star_glob_pattern("\\*salu**t", "\\*salu*t"); + test_one_normalize_star_glob_pattern("\\*salut**", "\\*salut*"); + test_one_normalize_star_glob_pattern("\\*salut**\\*", "\\*salut*\\*"); + test_one_normalize_star_glob_pattern("\\*salut", "\\*salut"); + test_one_normalize_star_glob_pattern("\\***salut", "\\**salut"); + test_one_normalize_star_glob_pattern("salut\\", "salut\\"); + test_one_normalize_star_glob_pattern("salut\\**", "salut\\**"); + test_one_normalize_star_glob_pattern("salut\\\\*", "salut\\\\*"); + test_one_normalize_star_glob_pattern("salut\\\\***", "salut\\\\*"); + test_one_normalize_star_glob_pattern("*", "*"); + test_one_normalize_star_glob_pattern("**", "*"); + test_one_normalize_star_glob_pattern("***", "*"); + test_one_normalize_star_glob_pattern("**\\***", "*\\**"); +} + +int main(int argc, char **argv) +{ + plan_tests(NUM_TESTS); + diag("String utils unit tests"); + test_normalize_star_glob_pattern(); + test_is_star_glob_pattern(); + test_is_star_at_the_end_only_glob_pattern(); + test_split(); + + return exit_status(); +} diff --git a/tests/unit/test_unix_socket.c b/tests/unit/test_unix_socket.c deleted file mode 100644 index 8752ba941..000000000 --- a/tests/unit/test_unix_socket.c +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS -#define MESSAGE_COUNT 4 -#define LARGE_PAYLOAD_SIZE 4 * 1024 -#define LARGE_PAYLOAD_RECV_SIZE 100 - -static const int TEST_COUNT = 37; - -/* For error.h */ -int lttng_opt_quiet; -int lttng_opt_verbose; -int lttng_opt_mi; - -/* - * Validate that a large number of file descriptors can be received in one shot. - */ -static void test_high_fd_count(unsigned int fd_count) -{ - int sockets[2] = {-1, -1}; - int ret; - unsigned int i; - const unsigned int payload_content = 42; - struct lttng_payload sent_payload; - struct lttng_payload received_payload; - - diag("Send and receive high FD count atomically (%u FDs)", fd_count); - lttng_payload_init(&sent_payload); - lttng_payload_init(&received_payload); - - ret = lttcomm_create_anon_unix_socketpair(sockets); - ok(ret == 0, "Created anonymous unix socket pair"); - if (ret < 0) { - PERROR("Failed to create an anonymous pair of unix sockets"); - goto error; - } - - /* Add dummy content to payload. */ - ret = lttng_dynamic_buffer_append(&sent_payload.buffer, - &payload_content, sizeof(payload_content)); - if (ret) { - PERROR("Failed to initialize test payload"); - goto error; - } - - for (i = 0; i < fd_count; i++) { - struct fd_handle *handle; - int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); - - if (fd < 0) { - PERROR("Failed to create fd while creating test payload"); - goto error; - } - - handle = fd_handle_create(fd); - if (!handle) { - if (close(fd)) { - PERROR("Failed to close fd while preparing test payload"); - goto error; - } - } - - ret = lttng_payload_push_fd_handle(&sent_payload, handle); - fd_handle_put(handle); - if (ret) { - PERROR("Failed to add fd handle to test payload"); - goto error; - } - } - - /* Send payload. */ - { - ssize_t sock_ret; - struct lttng_payload_view pv = lttng_payload_view_from_payload( - &sent_payload, 0, -1); - - /* Not expected to block considering the size of the payload. */ - sock_ret = lttcomm_send_unix_sock( - sockets[0], pv.buffer.data, pv.buffer.size); - ok(sock_ret == pv.buffer.size, "Sent complete test payload"); - if (sock_ret != pv.buffer.size) { - ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", - sock_ret, pv.buffer.size); - goto error; - } - - sock_ret = lttcomm_send_payload_view_fds_unix_sock( - sockets[0], &pv); - ok(sock_ret == 1, "Sent test payload file descriptors"); - if (sock_ret != 1) { - if (sock_ret < 0) { - PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", - sock_ret, 1); - } else { - diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", - sock_ret, 1); - } - - goto error; - } - } - - /* Receive payload */ - { - ssize_t sock_ret; - - ret = lttng_dynamic_buffer_set_size(&received_payload.buffer, - sent_payload.buffer.size); - if (ret) { - PERROR("Failed to pre-allocate reception buffer"); - goto error; - } - - sock_ret = lttcomm_recv_unix_sock(sockets[1], - received_payload.buffer.data, - received_payload.buffer.size); - ok(sock_ret == received_payload.buffer.size, - "Received payload bytes"); - if (sock_ret != received_payload.buffer.size) { - ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", - sock_ret, received_payload.buffer.size); - goto error; - } - - sock_ret = lttcomm_recv_payload_fds_unix_sock( - sockets[1], fd_count, &received_payload); - ok(sock_ret == (int) (sizeof(int) * fd_count), - "FD reception return value is number of fd * sizeof(int)"); - if (sock_ret != (int) (sizeof(int) * fd_count)) { - ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d", - sock_ret, - (int) (fd_count * sizeof(int))); - goto error; - } - - { - const struct lttng_payload_view pv = - lttng_payload_view_from_payload( - &received_payload, 0, - -1); - const int fd_handle_count = - lttng_payload_view_get_fd_handle_count( - &pv); - - ok(fd_handle_count == fd_count, - "Received all test payload file descriptors in one invocation"); - } - } - -error: - for (i = 0; i < 2; i++) { - if (sockets[i] < 0) { - continue; - } - - if (close(sockets[i])) { - PERROR("Failed to close unix socket"); - } - } - - lttng_payload_reset(&sent_payload); - lttng_payload_reset(&received_payload); -} - -/* - * Validate that if the sender sent multiple messages, each containing 1 fd, - * the receiver can receive one message at a time (the binary payload and its - * fd) and is not forced to receive all file descriptors at once. - */ -static void test_one_fd_per_message(unsigned int message_count) -{ - const unsigned int payload_content = 42; - int sockets[2] = {-1, -1}; - int ret; - unsigned int i; - struct lttng_payload sent_payload; - struct lttng_payload received_payload; - - diag("Send and receive small messages with one FD each (%u messages)", - message_count); - lttng_payload_init(&sent_payload); - lttng_payload_init(&received_payload); - - ret = lttcomm_create_anon_unix_socketpair(sockets); - ok(ret == 0, "Created anonymous unix socket pair"); - if (ret < 0) { - PERROR("Failed to create an anonymous pair of unix sockets"); - goto error; - } - - /* Send messages with one fd each. */ - for (i = 0; i < message_count; i++) { - struct fd_handle *handle; - int fd; - - /* Add dummy content to payload. */ - ret = lttng_dynamic_buffer_append(&sent_payload.buffer, - &payload_content, sizeof(payload_content)); - if (ret) { - PERROR("Failed to initialize test payload"); - goto error; - } - - fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); - if (fd < 0) { - PERROR("Failed to create fd while creating test payload"); - goto error; - } - - handle = fd_handle_create(fd); - if (!handle) { - if (close(fd)) { - PERROR("Failed to close fd while preparing test payload"); - goto error; - } - } - - ret = lttng_payload_push_fd_handle(&sent_payload, handle); - fd_handle_put(handle); - if (ret) { - PERROR("Failed to add fd handle to test payload"); - goto error; - } - - /* Send payload. */ - { - ssize_t sock_ret; - struct lttng_payload_view pv = - lttng_payload_view_from_payload( - &sent_payload, 0, -1); - - /* Not expected to block considering the size of the - * payload. */ - sock_ret = lttcomm_send_unix_sock(sockets[0], - pv.buffer.data, pv.buffer.size); - ok(sock_ret == pv.buffer.size, - "Sent binary payload for message %u", - i); - if (sock_ret != pv.buffer.size) { - ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", - sock_ret, pv.buffer.size); - goto error; - } - - sock_ret = lttcomm_send_payload_view_fds_unix_sock( - sockets[0], &pv); - ok(sock_ret == 1, - "Sent file descriptors payload for message %u", - i); - if (sock_ret != 1) { - if (sock_ret < 0) { - PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", - sock_ret, 1); - } else { - diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", - sock_ret, 1); - } - - goto error; - } - } - - lttng_payload_clear(&sent_payload); - } - - /* Receive messages one at a time. */ - for (i = 0; i < message_count; i++) { - ssize_t sock_ret; - - ret = lttng_dynamic_buffer_set_size(&received_payload.buffer, - sizeof(payload_content)); - if (ret) { - PERROR("Failed to pre-allocate reception buffer"); - goto error; - } - - sock_ret = lttcomm_recv_unix_sock(sockets[1], - received_payload.buffer.data, - received_payload.buffer.size); - ok(sock_ret == received_payload.buffer.size, - "Received payload bytes for message %u", i); - if (sock_ret != received_payload.buffer.size) { - ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", - sock_ret, received_payload.buffer.size); - goto error; - } - - sock_ret = lttcomm_recv_payload_fds_unix_sock( - sockets[1], 1, &received_payload); - ok(sock_ret == (int) sizeof(int), "Received fd for message %u", - i); - if (sock_ret != (int) sizeof(int)) { - ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u", - sock_ret, (int) sizeof(int)); - goto error; - } - - { - const struct lttng_payload_view pv = - lttng_payload_view_from_payload( - &received_payload, 0, - -1); - const int fd_handle_count = - lttng_payload_view_get_fd_handle_count( - &pv); - - ok(fd_handle_count == 1, - "Payload contains 1 fd for message %u", - i); - } - - lttng_payload_clear(&received_payload); - } - -error: - for (i = 0; i < 2; i++) { - if (sockets[i] < 0) { - continue; - } - - if (close(sockets[i])) { - PERROR("Failed to close unix socket"); - } - } - - lttng_payload_reset(&sent_payload); - lttng_payload_reset(&received_payload); -} - -/* - * Validate that a large message can be received in multiple chunks. - */ -static void test_receive_in_chunks( - unsigned int payload_size, unsigned int max_recv_size) -{ - int sockets[2] = {-1, -1}; - int ret; - unsigned int i; - struct lttng_payload sent_payload; - struct lttng_payload received_payload; - struct fd_handle *handle; - int fd; - ssize_t sock_ret, received = 0; - - diag("Receive a message in multiple chunks"); - lttng_payload_init(&sent_payload); - lttng_payload_init(&received_payload); - - ret = lttcomm_create_anon_unix_socketpair(sockets); - ok(ret == 0, "Created anonymous unix socket pair"); - if (ret < 0) { - PERROR("Failed to create an anonymous pair of unix sockets"); - goto error; - } - - /* Add dummy content to payload. */ - ret = lttng_dynamic_buffer_set_size(&sent_payload.buffer, payload_size); - if (ret) { - PERROR("Failed to initialize test payload"); - goto error; - } - - fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); - if (fd < 0) { - PERROR("Failed to create fd while creating test payload"); - goto error; - } - - handle = fd_handle_create(fd); - if (!handle) { - if (close(fd)) { - PERROR("Failed to close fd while preparing test payload"); - goto error; - } - } - - ret = lttng_payload_push_fd_handle(&sent_payload, handle); - fd_handle_put(handle); - if (ret) { - PERROR("Failed to add fd handle to test payload"); - goto error; - } - - /* Send payload. */ - { - struct lttng_payload_view pv = lttng_payload_view_from_payload( - &sent_payload, 0, -1); - - /* Not expected to block considering the size of the payload. */ - sock_ret = lttcomm_send_unix_sock( - sockets[0], pv.buffer.data, pv.buffer.size); - ok(sock_ret == pv.buffer.size, "Sent complete test payload"); - if (sock_ret != pv.buffer.size) { - ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", - sock_ret, pv.buffer.size); - goto error; - } - - sock_ret = lttcomm_send_payload_view_fds_unix_sock( - sockets[0], &pv); - ok(sock_ret == 1, "Sent test payload file descriptors"); - if (sock_ret != 1) { - if (sock_ret < 0) { - PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", - sock_ret, 1); - } else { - diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", - sock_ret, 1); - } - - goto error; - } - } - - /* Receive payload */ - ret = lttng_dynamic_buffer_set_size( - &received_payload.buffer, sent_payload.buffer.size); - if (ret) { - PERROR("Failed to pre-allocate reception buffer"); - goto error; - } - - do { - const ssize_t to_receive_this_pass = min(max_recv_size, - sent_payload.buffer.size - received); - - sock_ret = lttcomm_recv_unix_sock(sockets[1], - received_payload.buffer.data + received, - to_receive_this_pass); - if (sock_ret != to_receive_this_pass) { - ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", - sock_ret, to_receive_this_pass); - break; - } - - received += sock_ret; - } while (received < sent_payload.buffer.size); - - ok(received == sent_payload.buffer.size, - "Received complete payload in chunks of %u bytes", - max_recv_size); - if (received != sent_payload.buffer.size) { - goto error; - } - - sock_ret = lttcomm_recv_payload_fds_unix_sock( - sockets[1], 1, &received_payload); - ok(sock_ret == (int) sizeof(int), - "Received file descriptor after receiving payload in chunks"); - if (sock_ret != (int) sizeof(int)) { - ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d", - sock_ret, (int) sizeof(int)); - goto error; - } - - { - const struct lttng_payload_view pv = - lttng_payload_view_from_payload( - &received_payload, 0, -1); - const int fd_handle_count = - lttng_payload_view_get_fd_handle_count(&pv); - - ok(fd_handle_count == 1, - "Payload contains 1 fd after receiving payload in chunks"); - } - -error: - for (i = 0; i < 2; i++) { - if (sockets[i] < 0) { - continue; - } - - if (close(sockets[i])) { - PERROR("Failed to close unix socket"); - } - } - - lttng_payload_reset(&sent_payload); - lttng_payload_reset(&received_payload); -} - -static -void test_creds_passing(void) -{ - pid_t fork_ret = -1; - int ret, parent_socket = -1, child_connection_socket = -1; - ssize_t sock_ret; - char socket_dir_path[] = "/tmp/test.unix.socket.creds.passing.XXXXXX"; - char socket_path[PATH_MAX] = {}; - struct expected_creds { - uid_t euid; - gid_t egid; - pid_t pid; - } expected_creds; - - diag("Receive peer's effective uid, effective gid, and pid from a unix socket"); - - if (!mkdtemp(socket_dir_path)) { - PERROR("Failed to generate temporary socket location"); - goto error; - } - - strncat(socket_path, socket_dir_path, - sizeof(socket_path) - strlen(socket_path) - 1); - strncat(socket_path, "/test_unix_socket", - sizeof(socket_path) - strlen(socket_path) - 1); - - parent_socket = lttcomm_create_unix_sock(socket_path); - ok(parent_socket >= 0, "Created unix socket at path `%s`", socket_path); - if (parent_socket < 0) { - PERROR("Failed to create unix socket at path `%s`", socket_path); - goto error; - } - - ret = lttcomm_listen_unix_sock(parent_socket); - if (ret < 0) { - PERROR("Failed to mark parent socket as a passive socket"); - goto error; - } - - ret = lttcomm_setsockopt_creds_unix_sock(parent_socket); - if (ret) { - PERROR("Failed to set SO_PASSCRED on parent socket"); - goto error; - } - - fork_ret = fork(); - if (fork_ret < 0) { - PERROR("Failed to fork"); - goto error; - } - - if (fork_ret == 0) { - /* Child. */ - int child_socket; - - expected_creds = (struct expected_creds){ - .euid = geteuid(), - .egid = getegid(), - .pid = getpid(), - }; - - child_socket = lttcomm_connect_unix_sock(socket_path); - if (child_socket < 0) { - PERROR("Failed to connect to parent socket"); - goto error; - } - - ret = lttcomm_setsockopt_creds_unix_sock(child_socket); - if (ret) { - PERROR("Failed to set SO_PASSCRED on child socket"); - } - - sock_ret = lttcomm_send_creds_unix_sock(child_socket, &expected_creds, - sizeof(expected_creds)); - if (sock_ret < 0) { - PERROR("Failed to send expected credentials"); - } - - ret = close(child_socket); - if (ret) { - PERROR("Failed to close child socket"); - } - } else { - /* Parent. */ - int child_status; - pid_t wait_pid_ret; - lttng_sock_cred received_creds = {}; - - child_connection_socket = - lttcomm_accept_unix_sock(parent_socket); - if (child_connection_socket < 0) { - PERROR(); - goto error; - } - - ret = lttcomm_setsockopt_creds_unix_sock( - child_connection_socket); - if (ret) { - PERROR("Failed to set SO_PASSCRED on child connection socket"); - goto error; - } - - sock_ret = lttcomm_recv_creds_unix_sock(child_connection_socket, - &expected_creds, sizeof(expected_creds), - &received_creds); - if (sock_ret < 0) { - PERROR("Failed to receive credentials"); - goto error; - } - - wait_pid_ret = waitpid(fork_ret, &child_status, 0); - if (wait_pid_ret == -1) { - PERROR("Failed to wait for termination of child process"); - goto error; - } - if (!WIFEXITED(child_status) || WEXITSTATUS(child_status)) { - diag("Child process reported an error, test failed"); - goto error; - } - - ok(expected_creds.euid == received_creds.uid, - "Received the expected effective uid (%d == %d)", - expected_creds.euid, received_creds.uid); - ok(expected_creds.egid == received_creds.gid, - "Received the expected effective gid (%d == %d)", - expected_creds.egid, received_creds.gid); - ok(expected_creds.pid == received_creds.pid, - "Received the expected pid (%d == %d)", - expected_creds.pid, received_creds.pid); - } - -error: - if (parent_socket >= 0) { - ret = close(parent_socket); - if (ret) { - PERROR("Failed to close parent socket"); - } - } - - if (fork_ret == 0) { - /* Prevent libtap from printing a result for the child. */ - fclose(stdout); - fclose(stderr); - - /* Child exits at the end of this test. */ - exit(0); - } else if (parent_socket >= 0) { - if (child_connection_socket >= 0) { - ret = close(child_connection_socket); - if (ret) { - PERROR("Failed to close child connection socket"); - } - } - - ret = unlink(socket_path); - if (ret) { - PERROR("Failed to unlink socket at path `%s`", - socket_path); - } - - ret = rmdir(socket_dir_path); - if (ret) { - PERROR("Failed to remove test directory at `%s`", - socket_dir_path); - } - } -} - -int main(void) -{ - plan_tests(TEST_COUNT); - - test_high_fd_count(HIGH_FD_COUNT); - test_one_fd_per_message(MESSAGE_COUNT); - test_receive_in_chunks(LARGE_PAYLOAD_SIZE, LARGE_PAYLOAD_RECV_SIZE); - test_creds_passing(); - - return exit_status(); -} diff --git a/tests/unit/test_unix_socket.cpp b/tests/unit/test_unix_socket.cpp new file mode 100644 index 000000000..2d59ba8ee --- /dev/null +++ b/tests/unit/test_unix_socket.cpp @@ -0,0 +1,684 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS +#define MESSAGE_COUNT 4 +#define LARGE_PAYLOAD_SIZE 4 * 1024 +#define LARGE_PAYLOAD_RECV_SIZE 100 + +static const int TEST_COUNT = 37; + +/* For error.h */ +int lttng_opt_quiet; +int lttng_opt_verbose; +int lttng_opt_mi; + +/* + * Validate that a large number of file descriptors can be received in one shot. + */ +static void test_high_fd_count(unsigned int fd_count) +{ + int sockets[2] = {-1, -1}; + int ret; + unsigned int i; + const unsigned int payload_content = 42; + struct lttng_payload sent_payload; + struct lttng_payload received_payload; + + diag("Send and receive high FD count atomically (%u FDs)", fd_count); + lttng_payload_init(&sent_payload); + lttng_payload_init(&received_payload); + + ret = lttcomm_create_anon_unix_socketpair(sockets); + ok(ret == 0, "Created anonymous unix socket pair"); + if (ret < 0) { + PERROR("Failed to create an anonymous pair of unix sockets"); + goto error; + } + + /* Add dummy content to payload. */ + ret = lttng_dynamic_buffer_append(&sent_payload.buffer, + &payload_content, sizeof(payload_content)); + if (ret) { + PERROR("Failed to initialize test payload"); + goto error; + } + + for (i = 0; i < fd_count; i++) { + struct fd_handle *handle; + int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); + + if (fd < 0) { + PERROR("Failed to create fd while creating test payload"); + goto error; + } + + handle = fd_handle_create(fd); + if (!handle) { + if (close(fd)) { + PERROR("Failed to close fd while preparing test payload"); + goto error; + } + } + + ret = lttng_payload_push_fd_handle(&sent_payload, handle); + fd_handle_put(handle); + if (ret) { + PERROR("Failed to add fd handle to test payload"); + goto error; + } + } + + /* Send payload. */ + { + ssize_t sock_ret; + struct lttng_payload_view pv = lttng_payload_view_from_payload( + &sent_payload, 0, -1); + + /* Not expected to block considering the size of the payload. */ + sock_ret = lttcomm_send_unix_sock( + sockets[0], pv.buffer.data, pv.buffer.size); + ok(sock_ret == pv.buffer.size, "Sent complete test payload"); + if (sock_ret != pv.buffer.size) { + ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", + sock_ret, pv.buffer.size); + goto error; + } + + sock_ret = lttcomm_send_payload_view_fds_unix_sock( + sockets[0], &pv); + ok(sock_ret == 1, "Sent test payload file descriptors"); + if (sock_ret != 1) { + if (sock_ret < 0) { + PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", + sock_ret, 1); + } else { + diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", + sock_ret, 1); + } + + goto error; + } + } + + /* Receive payload */ + { + ssize_t sock_ret; + + ret = lttng_dynamic_buffer_set_size(&received_payload.buffer, + sent_payload.buffer.size); + if (ret) { + PERROR("Failed to pre-allocate reception buffer"); + goto error; + } + + sock_ret = lttcomm_recv_unix_sock(sockets[1], + received_payload.buffer.data, + received_payload.buffer.size); + ok(sock_ret == received_payload.buffer.size, + "Received payload bytes"); + if (sock_ret != received_payload.buffer.size) { + ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", + sock_ret, received_payload.buffer.size); + goto error; + } + + sock_ret = lttcomm_recv_payload_fds_unix_sock( + sockets[1], fd_count, &received_payload); + ok(sock_ret == (int) (sizeof(int) * fd_count), + "FD reception return value is number of fd * sizeof(int)"); + if (sock_ret != (int) (sizeof(int) * fd_count)) { + ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d", + sock_ret, + (int) (fd_count * sizeof(int))); + goto error; + } + + { + const struct lttng_payload_view pv = + lttng_payload_view_from_payload( + &received_payload, 0, + -1); + const int fd_handle_count = + lttng_payload_view_get_fd_handle_count( + &pv); + + ok(fd_handle_count == fd_count, + "Received all test payload file descriptors in one invocation"); + } + } + +error: + for (i = 0; i < 2; i++) { + if (sockets[i] < 0) { + continue; + } + + if (close(sockets[i])) { + PERROR("Failed to close unix socket"); + } + } + + lttng_payload_reset(&sent_payload); + lttng_payload_reset(&received_payload); +} + +/* + * Validate that if the sender sent multiple messages, each containing 1 fd, + * the receiver can receive one message at a time (the binary payload and its + * fd) and is not forced to receive all file descriptors at once. + */ +static void test_one_fd_per_message(unsigned int message_count) +{ + const unsigned int payload_content = 42; + int sockets[2] = {-1, -1}; + int ret; + unsigned int i; + struct lttng_payload sent_payload; + struct lttng_payload received_payload; + + diag("Send and receive small messages with one FD each (%u messages)", + message_count); + lttng_payload_init(&sent_payload); + lttng_payload_init(&received_payload); + + ret = lttcomm_create_anon_unix_socketpair(sockets); + ok(ret == 0, "Created anonymous unix socket pair"); + if (ret < 0) { + PERROR("Failed to create an anonymous pair of unix sockets"); + goto error; + } + + /* Send messages with one fd each. */ + for (i = 0; i < message_count; i++) { + struct fd_handle *handle; + int fd; + + /* Add dummy content to payload. */ + ret = lttng_dynamic_buffer_append(&sent_payload.buffer, + &payload_content, sizeof(payload_content)); + if (ret) { + PERROR("Failed to initialize test payload"); + goto error; + } + + fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); + if (fd < 0) { + PERROR("Failed to create fd while creating test payload"); + goto error; + } + + handle = fd_handle_create(fd); + if (!handle) { + if (close(fd)) { + PERROR("Failed to close fd while preparing test payload"); + goto error; + } + } + + ret = lttng_payload_push_fd_handle(&sent_payload, handle); + fd_handle_put(handle); + if (ret) { + PERROR("Failed to add fd handle to test payload"); + goto error; + } + + /* Send payload. */ + { + ssize_t sock_ret; + struct lttng_payload_view pv = + lttng_payload_view_from_payload( + &sent_payload, 0, -1); + + /* Not expected to block considering the size of the + * payload. */ + sock_ret = lttcomm_send_unix_sock(sockets[0], + pv.buffer.data, pv.buffer.size); + ok(sock_ret == pv.buffer.size, + "Sent binary payload for message %u", + i); + if (sock_ret != pv.buffer.size) { + ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", + sock_ret, pv.buffer.size); + goto error; + } + + sock_ret = lttcomm_send_payload_view_fds_unix_sock( + sockets[0], &pv); + ok(sock_ret == 1, + "Sent file descriptors payload for message %u", + i); + if (sock_ret != 1) { + if (sock_ret < 0) { + PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", + sock_ret, 1); + } else { + diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", + sock_ret, 1); + } + + goto error; + } + } + + lttng_payload_clear(&sent_payload); + } + + /* Receive messages one at a time. */ + for (i = 0; i < message_count; i++) { + ssize_t sock_ret; + + ret = lttng_dynamic_buffer_set_size(&received_payload.buffer, + sizeof(payload_content)); + if (ret) { + PERROR("Failed to pre-allocate reception buffer"); + goto error; + } + + sock_ret = lttcomm_recv_unix_sock(sockets[1], + received_payload.buffer.data, + received_payload.buffer.size); + ok(sock_ret == received_payload.buffer.size, + "Received payload bytes for message %u", i); + if (sock_ret != received_payload.buffer.size) { + ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", + sock_ret, received_payload.buffer.size); + goto error; + } + + sock_ret = lttcomm_recv_payload_fds_unix_sock( + sockets[1], 1, &received_payload); + ok(sock_ret == (int) sizeof(int), "Received fd for message %u", + i); + if (sock_ret != (int) sizeof(int)) { + ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u", + sock_ret, (int) sizeof(int)); + goto error; + } + + { + const struct lttng_payload_view pv = + lttng_payload_view_from_payload( + &received_payload, 0, + -1); + const int fd_handle_count = + lttng_payload_view_get_fd_handle_count( + &pv); + + ok(fd_handle_count == 1, + "Payload contains 1 fd for message %u", + i); + } + + lttng_payload_clear(&received_payload); + } + +error: + for (i = 0; i < 2; i++) { + if (sockets[i] < 0) { + continue; + } + + if (close(sockets[i])) { + PERROR("Failed to close unix socket"); + } + } + + lttng_payload_reset(&sent_payload); + lttng_payload_reset(&received_payload); +} + +/* + * Validate that a large message can be received in multiple chunks. + */ +static void test_receive_in_chunks( + unsigned int payload_size, unsigned int max_recv_size) +{ + int sockets[2] = {-1, -1}; + int ret; + unsigned int i; + struct lttng_payload sent_payload; + struct lttng_payload received_payload; + struct fd_handle *handle; + int fd; + ssize_t sock_ret, received = 0; + + diag("Receive a message in multiple chunks"); + lttng_payload_init(&sent_payload); + lttng_payload_init(&received_payload); + + ret = lttcomm_create_anon_unix_socketpair(sockets); + ok(ret == 0, "Created anonymous unix socket pair"); + if (ret < 0) { + PERROR("Failed to create an anonymous pair of unix sockets"); + goto error; + } + + /* Add dummy content to payload. */ + ret = lttng_dynamic_buffer_set_size(&sent_payload.buffer, payload_size); + if (ret) { + PERROR("Failed to initialize test payload"); + goto error; + } + + fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); + if (fd < 0) { + PERROR("Failed to create fd while creating test payload"); + goto error; + } + + handle = fd_handle_create(fd); + if (!handle) { + if (close(fd)) { + PERROR("Failed to close fd while preparing test payload"); + goto error; + } + } + + ret = lttng_payload_push_fd_handle(&sent_payload, handle); + fd_handle_put(handle); + if (ret) { + PERROR("Failed to add fd handle to test payload"); + goto error; + } + + /* Send payload. */ + { + struct lttng_payload_view pv = lttng_payload_view_from_payload( + &sent_payload, 0, -1); + + /* Not expected to block considering the size of the payload. */ + sock_ret = lttcomm_send_unix_sock( + sockets[0], pv.buffer.data, pv.buffer.size); + ok(sock_ret == pv.buffer.size, "Sent complete test payload"); + if (sock_ret != pv.buffer.size) { + ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", + sock_ret, pv.buffer.size); + goto error; + } + + sock_ret = lttcomm_send_payload_view_fds_unix_sock( + sockets[0], &pv); + ok(sock_ret == 1, "Sent test payload file descriptors"); + if (sock_ret != 1) { + if (sock_ret < 0) { + PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", + sock_ret, 1); + } else { + diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", + sock_ret, 1); + } + + goto error; + } + } + + /* Receive payload */ + ret = lttng_dynamic_buffer_set_size( + &received_payload.buffer, sent_payload.buffer.size); + if (ret) { + PERROR("Failed to pre-allocate reception buffer"); + goto error; + } + + do { + const ssize_t to_receive_this_pass = + std::min(max_recv_size, + sent_payload.buffer.size - received); + + sock_ret = lttcomm_recv_unix_sock(sockets[1], + received_payload.buffer.data + received, + to_receive_this_pass); + if (sock_ret != to_receive_this_pass) { + ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", + sock_ret, to_receive_this_pass); + break; + } + + received += sock_ret; + } while (received < sent_payload.buffer.size); + + ok(received == sent_payload.buffer.size, + "Received complete payload in chunks of %u bytes", + max_recv_size); + if (received != sent_payload.buffer.size) { + goto error; + } + + sock_ret = lttcomm_recv_payload_fds_unix_sock( + sockets[1], 1, &received_payload); + ok(sock_ret == (int) sizeof(int), + "Received file descriptor after receiving payload in chunks"); + if (sock_ret != (int) sizeof(int)) { + ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d", + sock_ret, (int) sizeof(int)); + goto error; + } + + { + const struct lttng_payload_view pv = + lttng_payload_view_from_payload( + &received_payload, 0, -1); + const int fd_handle_count = + lttng_payload_view_get_fd_handle_count(&pv); + + ok(fd_handle_count == 1, + "Payload contains 1 fd after receiving payload in chunks"); + } + +error: + for (i = 0; i < 2; i++) { + if (sockets[i] < 0) { + continue; + } + + if (close(sockets[i])) { + PERROR("Failed to close unix socket"); + } + } + + lttng_payload_reset(&sent_payload); + lttng_payload_reset(&received_payload); +} + +static +void test_creds_passing(void) +{ + pid_t fork_ret = -1; + int ret, parent_socket = -1, child_connection_socket = -1; + ssize_t sock_ret; + char socket_dir_path[] = "/tmp/test.unix.socket.creds.passing.XXXXXX"; + char socket_path[PATH_MAX] = {}; + struct expected_creds { + uid_t euid; + gid_t egid; + pid_t pid; + } expected_creds; + + diag("Receive peer's effective uid, effective gid, and pid from a unix socket"); + + if (!mkdtemp(socket_dir_path)) { + PERROR("Failed to generate temporary socket location"); + goto error; + } + + strncat(socket_path, socket_dir_path, + sizeof(socket_path) - strlen(socket_path) - 1); + strncat(socket_path, "/test_unix_socket", + sizeof(socket_path) - strlen(socket_path) - 1); + + parent_socket = lttcomm_create_unix_sock(socket_path); + ok(parent_socket >= 0, "Created unix socket at path `%s`", socket_path); + if (parent_socket < 0) { + PERROR("Failed to create unix socket at path `%s`", socket_path); + goto error; + } + + ret = lttcomm_listen_unix_sock(parent_socket); + if (ret < 0) { + PERROR("Failed to mark parent socket as a passive socket"); + goto error; + } + + ret = lttcomm_setsockopt_creds_unix_sock(parent_socket); + if (ret) { + PERROR("Failed to set SO_PASSCRED on parent socket"); + goto error; + } + + fork_ret = fork(); + if (fork_ret < 0) { + PERROR("Failed to fork"); + goto error; + } + + if (fork_ret == 0) { + /* Child. */ + int child_socket; + + expected_creds = (struct expected_creds){ + .euid = geteuid(), + .egid = getegid(), + .pid = getpid(), + }; + + child_socket = lttcomm_connect_unix_sock(socket_path); + if (child_socket < 0) { + PERROR("Failed to connect to parent socket"); + goto error; + } + + ret = lttcomm_setsockopt_creds_unix_sock(child_socket); + if (ret) { + PERROR("Failed to set SO_PASSCRED on child socket"); + } + + sock_ret = lttcomm_send_creds_unix_sock(child_socket, &expected_creds, + sizeof(expected_creds)); + if (sock_ret < 0) { + PERROR("Failed to send expected credentials"); + } + + ret = close(child_socket); + if (ret) { + PERROR("Failed to close child socket"); + } + } else { + /* Parent. */ + int child_status; + pid_t wait_pid_ret; + lttng_sock_cred received_creds = {}; + + child_connection_socket = + lttcomm_accept_unix_sock(parent_socket); + if (child_connection_socket < 0) { + PERROR(); + goto error; + } + + ret = lttcomm_setsockopt_creds_unix_sock( + child_connection_socket); + if (ret) { + PERROR("Failed to set SO_PASSCRED on child connection socket"); + goto error; + } + + sock_ret = lttcomm_recv_creds_unix_sock(child_connection_socket, + &expected_creds, sizeof(expected_creds), + &received_creds); + if (sock_ret < 0) { + PERROR("Failed to receive credentials"); + goto error; + } + + wait_pid_ret = waitpid(fork_ret, &child_status, 0); + if (wait_pid_ret == -1) { + PERROR("Failed to wait for termination of child process"); + goto error; + } + if (!WIFEXITED(child_status) || WEXITSTATUS(child_status)) { + diag("Child process reported an error, test failed"); + goto error; + } + + ok(expected_creds.euid == received_creds.uid, + "Received the expected effective uid (%d == %d)", + expected_creds.euid, received_creds.uid); + ok(expected_creds.egid == received_creds.gid, + "Received the expected effective gid (%d == %d)", + expected_creds.egid, received_creds.gid); + ok(expected_creds.pid == received_creds.pid, + "Received the expected pid (%d == %d)", + expected_creds.pid, received_creds.pid); + } + +error: + if (parent_socket >= 0) { + ret = close(parent_socket); + if (ret) { + PERROR("Failed to close parent socket"); + } + } + + if (fork_ret == 0) { + /* Prevent libtap from printing a result for the child. */ + fclose(stdout); + fclose(stderr); + + /* Child exits at the end of this test. */ + exit(0); + } else if (parent_socket >= 0) { + if (child_connection_socket >= 0) { + ret = close(child_connection_socket); + if (ret) { + PERROR("Failed to close child connection socket"); + } + } + + ret = unlink(socket_path); + if (ret) { + PERROR("Failed to unlink socket at path `%s`", + socket_path); + } + + ret = rmdir(socket_dir_path); + if (ret) { + PERROR("Failed to remove test directory at `%s`", + socket_dir_path); + } + } +} + +int main(void) +{ + plan_tests(TEST_COUNT); + + test_high_fd_count(HIGH_FD_COUNT); + test_one_fd_per_message(MESSAGE_COUNT); + test_receive_in_chunks(LARGE_PAYLOAD_SIZE, LARGE_PAYLOAD_RECV_SIZE); + test_creds_passing(); + + return exit_status(); +} diff --git a/tests/unit/test_uri.c b/tests/unit/test_uri.c deleted file mode 100644 index d2ad04b0c..000000000 --- a/tests/unit/test_uri.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include - -#include - -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose = 3; -int lttng_opt_mi; - -/* Number of TAP tests in this file */ -#define NUM_TESTS 11 - -static void test_uri_parsing(void) -{ - ssize_t size; - const char *s_uri1; - struct lttng_uri *uri = NULL; - - s_uri1 = "net://localhost"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 2 && - uri[0].dtype == LTTNG_DST_IPV4 && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 0 && - strlen(uri[0].subdir) == 0 && - strcmp(uri[0].dst.ipv4, "127.0.0.1") == 0 && - uri[1].dtype == LTTNG_DST_IPV4 && - uri[1].utype == LTTNG_URI_DST && - uri[1].stype == 0 && - uri[1].port == 0 && - strlen(uri[1].subdir) == 0 && - strcmp(uri[1].dst.ipv4, "127.0.0.1") == 0, - "URI set to net://localhost"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - s_uri1 = "net://localhost:8989:4242/my/test/path"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 2 && - uri[0].dtype == LTTNG_DST_IPV4 && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 8989 && - strcmp(uri[0].subdir, "my/test/path") == 0 && - strcmp(uri[0].dst.ipv4, "127.0.0.1") == 0 && - uri[1].dtype == LTTNG_DST_IPV4 && - uri[1].utype == LTTNG_URI_DST && - uri[1].stype == 0 && - uri[1].port == 4242 && - strlen(uri[1].subdir) == 0 && - strcmp(uri[1].dst.ipv4, "127.0.0.1") == 0, - "URI set to net://localhost:8989:4242/my/test/path"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - s_uri1 = "net://localhost:8989:4242"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 2 && - uri[0].dtype == LTTNG_DST_IPV4 && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 8989 && - strlen(uri[0].subdir) == 0 && - strcmp(uri[0].dst.ipv4, "127.0.0.1") == 0 && - uri[1].dtype == LTTNG_DST_IPV4 && - uri[1].utype == LTTNG_URI_DST && - uri[1].stype == 0 && - uri[1].port == 4242 && - strlen(uri[1].subdir) == 0 && - strcmp(uri[1].dst.ipv4, "127.0.0.1") == 0, - "URI set to net://localhost:8989:4242"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - s_uri1 = "net6://[::1]:8989"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 2 && - uri[0].dtype == LTTNG_DST_IPV6 && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 8989 && - strlen(uri[0].subdir) == 0 && - strcmp(uri[0].dst.ipv6, "::1") == 0 && - uri[1].dtype == LTTNG_DST_IPV6 && - uri[1].utype == LTTNG_URI_DST && - uri[1].stype == 0 && - uri[1].port == 0 && - strlen(uri[1].subdir) == 0 && - strcmp(uri[1].dst.ipv6, "::1") == 0, - "URI set to net6://[::1]:8989"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - s_uri1 = "tcp://42.42.42.42/my/test/path"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 1 && - uri[0].dtype == LTTNG_DST_IPV4 && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 0 && - strcmp(uri[0].subdir, "my/test/path") == 0 && - strcmp(uri[0].dst.ipv4, "42.42.42.42") == 0, - "URI set to tcp://42.42.42.42/my/test/path"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - s_uri1 = "tcp6://[fe80::f66d:4ff:fe53:d220]/my/test/path"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 1 && - uri[0].dtype == LTTNG_DST_IPV6 && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 0 && - strcmp(uri[0].subdir, "my/test/path") == 0 && - strcmp(uri[0].dst.ipv6, "fe80::f66d:4ff:fe53:d220") == 0, - "URI set to tcp6://[fe80::f66d:4ff:fe53:d220]/my/test/path"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - s_uri1 = "file:///my/test/path"; - - size = uri_parse(s_uri1, &uri); - - ok(size == 1 && - uri[0].dtype == LTTNG_DST_PATH && - uri[0].utype == LTTNG_URI_DST && - uri[0].stype == 0 && - uri[0].port == 0 && - strlen(uri[0].subdir) == 0 && - strcmp(uri[0].dst.path, "/my/test/path") == 0, - "URI set to file:///my/test/path"); - - if (uri) { - uri_free(uri); - uri = NULL; - } - - /* FIXME: Noisy on stdout */ - s_uri1 = "file/my/test/path"; - size = uri_parse(s_uri1, &uri); - ok(size == -1, "Bad URI set to file/my/test/path"); - LTTNG_ASSERT(!uri); - - s_uri1 = "net://:8999"; - size = uri_parse(s_uri1, &uri); - ok(size == -1, "Bad URI set to net://:8999"); - LTTNG_ASSERT(!uri); -} - -static void test_uri_cmp(void) -{ - struct lttng_uri *uri1, *uri2; - const char *s_uri1 = "net://localhost"; - const char *s_uri2 = "net://localhost:8989:4242"; - ssize_t size1, size2; - int res; - - size1 = uri_parse(s_uri1, &uri1); - - /* Sanity checks */ - LTTNG_ASSERT(size1 == 2); - LTTNG_ASSERT(uri1[0].dtype == LTTNG_DST_IPV4); - LTTNG_ASSERT(uri1[0].utype == LTTNG_URI_DST); - LTTNG_ASSERT(uri1[0].stype == 0); - LTTNG_ASSERT(uri1[0].port == 0); - LTTNG_ASSERT(strlen(uri1[0].subdir) == 0); - LTTNG_ASSERT(strcmp(uri1[0].dst.ipv4, "127.0.0.1") == 0); - LTTNG_ASSERT(uri1[1].dtype == LTTNG_DST_IPV4); - LTTNG_ASSERT(uri1[1].utype == LTTNG_URI_DST); - LTTNG_ASSERT(uri1[1].stype == 0); - LTTNG_ASSERT(uri1[1].port == 0); - LTTNG_ASSERT(strlen(uri1[1].subdir) == 0); - LTTNG_ASSERT(strcmp(uri1[1].dst.ipv4, "127.0.0.1") == 0); - - size2 = uri_parse(s_uri2, &uri2); - - LTTNG_ASSERT(size2 == 2); - LTTNG_ASSERT(uri2[0].dtype == LTTNG_DST_IPV4); - LTTNG_ASSERT(uri2[0].utype == LTTNG_URI_DST); - LTTNG_ASSERT(uri2[0].stype == 0); - LTTNG_ASSERT(uri2[0].port == 8989); - LTTNG_ASSERT(strlen(uri2[0].subdir) == 0); - LTTNG_ASSERT(strcmp(uri2[0].dst.ipv4, "127.0.0.1") == 0); - LTTNG_ASSERT(uri2[1].dtype == LTTNG_DST_IPV4); - LTTNG_ASSERT(uri2[1].utype == LTTNG_URI_DST); - LTTNG_ASSERT(uri2[1].stype == 0); - LTTNG_ASSERT(uri2[1].port == 4242); - LTTNG_ASSERT(strlen(uri2[1].subdir) == 0); - LTTNG_ASSERT(strcmp(uri2[1].dst.ipv4, "127.0.0.1") == 0); - - res = uri_compare(uri1, uri1); - - ok(res == 0, - "URI compare net://localhost == net://localhost"); - - res = uri_compare(uri1, uri2); - - ok(res != 0, - "URI compare net://localhost != net://localhost:8989:4242"); - - uri_free(uri1); - uri_free(uri2); -} - -int main(int argc, char **argv) -{ - plan_tests(NUM_TESTS); - - diag("URI unit tests"); - - test_uri_parsing(); - - test_uri_cmp(); - - return exit_status(); -} diff --git a/tests/unit/test_uri.cpp b/tests/unit/test_uri.cpp new file mode 100644 index 000000000..d2ad04b0c --- /dev/null +++ b/tests/unit/test_uri.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2012 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include + +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 3; +int lttng_opt_mi; + +/* Number of TAP tests in this file */ +#define NUM_TESTS 11 + +static void test_uri_parsing(void) +{ + ssize_t size; + const char *s_uri1; + struct lttng_uri *uri = NULL; + + s_uri1 = "net://localhost"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 2 && + uri[0].dtype == LTTNG_DST_IPV4 && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 0 && + strlen(uri[0].subdir) == 0 && + strcmp(uri[0].dst.ipv4, "127.0.0.1") == 0 && + uri[1].dtype == LTTNG_DST_IPV4 && + uri[1].utype == LTTNG_URI_DST && + uri[1].stype == 0 && + uri[1].port == 0 && + strlen(uri[1].subdir) == 0 && + strcmp(uri[1].dst.ipv4, "127.0.0.1") == 0, + "URI set to net://localhost"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + s_uri1 = "net://localhost:8989:4242/my/test/path"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 2 && + uri[0].dtype == LTTNG_DST_IPV4 && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 8989 && + strcmp(uri[0].subdir, "my/test/path") == 0 && + strcmp(uri[0].dst.ipv4, "127.0.0.1") == 0 && + uri[1].dtype == LTTNG_DST_IPV4 && + uri[1].utype == LTTNG_URI_DST && + uri[1].stype == 0 && + uri[1].port == 4242 && + strlen(uri[1].subdir) == 0 && + strcmp(uri[1].dst.ipv4, "127.0.0.1") == 0, + "URI set to net://localhost:8989:4242/my/test/path"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + s_uri1 = "net://localhost:8989:4242"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 2 && + uri[0].dtype == LTTNG_DST_IPV4 && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 8989 && + strlen(uri[0].subdir) == 0 && + strcmp(uri[0].dst.ipv4, "127.0.0.1") == 0 && + uri[1].dtype == LTTNG_DST_IPV4 && + uri[1].utype == LTTNG_URI_DST && + uri[1].stype == 0 && + uri[1].port == 4242 && + strlen(uri[1].subdir) == 0 && + strcmp(uri[1].dst.ipv4, "127.0.0.1") == 0, + "URI set to net://localhost:8989:4242"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + s_uri1 = "net6://[::1]:8989"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 2 && + uri[0].dtype == LTTNG_DST_IPV6 && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 8989 && + strlen(uri[0].subdir) == 0 && + strcmp(uri[0].dst.ipv6, "::1") == 0 && + uri[1].dtype == LTTNG_DST_IPV6 && + uri[1].utype == LTTNG_URI_DST && + uri[1].stype == 0 && + uri[1].port == 0 && + strlen(uri[1].subdir) == 0 && + strcmp(uri[1].dst.ipv6, "::1") == 0, + "URI set to net6://[::1]:8989"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + s_uri1 = "tcp://42.42.42.42/my/test/path"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 1 && + uri[0].dtype == LTTNG_DST_IPV4 && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 0 && + strcmp(uri[0].subdir, "my/test/path") == 0 && + strcmp(uri[0].dst.ipv4, "42.42.42.42") == 0, + "URI set to tcp://42.42.42.42/my/test/path"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + s_uri1 = "tcp6://[fe80::f66d:4ff:fe53:d220]/my/test/path"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 1 && + uri[0].dtype == LTTNG_DST_IPV6 && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 0 && + strcmp(uri[0].subdir, "my/test/path") == 0 && + strcmp(uri[0].dst.ipv6, "fe80::f66d:4ff:fe53:d220") == 0, + "URI set to tcp6://[fe80::f66d:4ff:fe53:d220]/my/test/path"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + s_uri1 = "file:///my/test/path"; + + size = uri_parse(s_uri1, &uri); + + ok(size == 1 && + uri[0].dtype == LTTNG_DST_PATH && + uri[0].utype == LTTNG_URI_DST && + uri[0].stype == 0 && + uri[0].port == 0 && + strlen(uri[0].subdir) == 0 && + strcmp(uri[0].dst.path, "/my/test/path") == 0, + "URI set to file:///my/test/path"); + + if (uri) { + uri_free(uri); + uri = NULL; + } + + /* FIXME: Noisy on stdout */ + s_uri1 = "file/my/test/path"; + size = uri_parse(s_uri1, &uri); + ok(size == -1, "Bad URI set to file/my/test/path"); + LTTNG_ASSERT(!uri); + + s_uri1 = "net://:8999"; + size = uri_parse(s_uri1, &uri); + ok(size == -1, "Bad URI set to net://:8999"); + LTTNG_ASSERT(!uri); +} + +static void test_uri_cmp(void) +{ + struct lttng_uri *uri1, *uri2; + const char *s_uri1 = "net://localhost"; + const char *s_uri2 = "net://localhost:8989:4242"; + ssize_t size1, size2; + int res; + + size1 = uri_parse(s_uri1, &uri1); + + /* Sanity checks */ + LTTNG_ASSERT(size1 == 2); + LTTNG_ASSERT(uri1[0].dtype == LTTNG_DST_IPV4); + LTTNG_ASSERT(uri1[0].utype == LTTNG_URI_DST); + LTTNG_ASSERT(uri1[0].stype == 0); + LTTNG_ASSERT(uri1[0].port == 0); + LTTNG_ASSERT(strlen(uri1[0].subdir) == 0); + LTTNG_ASSERT(strcmp(uri1[0].dst.ipv4, "127.0.0.1") == 0); + LTTNG_ASSERT(uri1[1].dtype == LTTNG_DST_IPV4); + LTTNG_ASSERT(uri1[1].utype == LTTNG_URI_DST); + LTTNG_ASSERT(uri1[1].stype == 0); + LTTNG_ASSERT(uri1[1].port == 0); + LTTNG_ASSERT(strlen(uri1[1].subdir) == 0); + LTTNG_ASSERT(strcmp(uri1[1].dst.ipv4, "127.0.0.1") == 0); + + size2 = uri_parse(s_uri2, &uri2); + + LTTNG_ASSERT(size2 == 2); + LTTNG_ASSERT(uri2[0].dtype == LTTNG_DST_IPV4); + LTTNG_ASSERT(uri2[0].utype == LTTNG_URI_DST); + LTTNG_ASSERT(uri2[0].stype == 0); + LTTNG_ASSERT(uri2[0].port == 8989); + LTTNG_ASSERT(strlen(uri2[0].subdir) == 0); + LTTNG_ASSERT(strcmp(uri2[0].dst.ipv4, "127.0.0.1") == 0); + LTTNG_ASSERT(uri2[1].dtype == LTTNG_DST_IPV4); + LTTNG_ASSERT(uri2[1].utype == LTTNG_URI_DST); + LTTNG_ASSERT(uri2[1].stype == 0); + LTTNG_ASSERT(uri2[1].port == 4242); + LTTNG_ASSERT(strlen(uri2[1].subdir) == 0); + LTTNG_ASSERT(strcmp(uri2[1].dst.ipv4, "127.0.0.1") == 0); + + res = uri_compare(uri1, uri1); + + ok(res == 0, + "URI compare net://localhost == net://localhost"); + + res = uri_compare(uri1, uri2); + + ok(res != 0, + "URI compare net://localhost != net://localhost:8989:4242"); + + uri_free(uri1); + uri_free(uri2); +} + +int main(int argc, char **argv) +{ + plan_tests(NUM_TESTS); + + diag("URI unit tests"); + + test_uri_parsing(); + + test_uri_cmp(); + + return exit_status(); +} diff --git a/tests/unit/test_utils_compat_poll.c b/tests/unit/test_utils_compat_poll.c deleted file mode 100644 index 2581f57dd..000000000 --- a/tests/unit/test_utils_compat_poll.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * test_utils_compat_poll.c - * - * Unit tests for the compatibility layer of poll/epoll API. - * - * Copyright (C) 2019 Yannick Lamarre - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -/* Verification without trashing test order in the child process */ -#define childok(e, test, ...) do { \ - if (!(e)) { \ - diag(test, ## __VA_ARGS__); \ - _exit(EXIT_FAILURE); \ - } \ -} while(0) - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose; -int lttng_opt_mi; - -/* - * Non-zero 8-bits arbitrary value below 0x7f to ensure no sign extension - * occurs. Used to verify that the value is properly propagated through the - * pipe. - */ -#define MAGIC_VALUE ((char) 0x5A) - -#ifdef HAVE_EPOLL -#define NUM_TESTS 48 -#else -#define NUM_TESTS 47 -#endif - -#ifdef HAVE_EPOLL -#if defined(HAVE_EPOLL_CREATE1) && defined(EPOLL_CLOEXEC) -#define CLOE_VALUE EPOLL_CLOEXEC -#else -#define CLOE_VALUE FD_CLOEXEC -#endif - -static -void test_epoll_compat(void) -{ - /* - * Type conversion present to disable warning of anonymous enum from - * compiler. - */ - ok((int) LTTNG_CLOEXEC == (int) CLOE_VALUE, "epoll's CLOEXEC value"); -} -#endif - -static void test_alloc(void) -{ - struct lttng_poll_event poll_events; - - lttng_poll_init(&poll_events); - - /* Null pointer */ - ok(lttng_poll_create(NULL, 1, 0) != 0, "Create over NULL pointer fails"); - /* Size 0 */ - ok(lttng_poll_create(&poll_events, 0, 0) != 0, "Create with size 0 fails"); - /* without CLOEXEC */ - ok(lttng_poll_create(&poll_events, 1, 0) == 0, "Create valid poll set succeeds"); - /* - * lttng_poll_event structure untested due to incompatibility across - * sublayers. lttng_poll_clean cannot be tested. There is no success - * criteria. Verify set's max size cases. - */ - lttng_poll_clean(&poll_events); -} - -/* Tests stuff related to what would be handled with epoll_ctl. */ -static void test_add_del(void) -{ - struct lttng_poll_event poll_events; - - lttng_poll_init(&poll_events); - ok(lttng_poll_add(NULL, 1, LPOLLIN) != 0, "Adding to NULL set fails"); - ok(lttng_poll_add(&poll_events, 1, LPOLLIN) != 0, "Adding to uninitialized structure fails"); - ok(lttng_poll_add(&poll_events, -1, LPOLLIN) != 0, "Adding invalid FD fails"); - - ok(lttng_poll_create(&poll_events, 1, 0) == 0, "Create a poll set succeeds"); - ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Set created empty"); - - ok(lttng_poll_add(NULL, 1, LPOLLIN) != 0, "Adding to NULL set fails"); - ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Set still empty"); - ok(lttng_poll_add(&poll_events, -1, LPOLLIN) != 0, "Adding invalid FD fails"); - ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Set still empty"); - - ok(lttng_poll_add(&poll_events, 1, LPOLLIN) == 0, "Adding valid FD succeeds"); - ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Nb of elements incremented"); - - ok(lttng_poll_del(NULL, 1) != 0, "Removing from NULL set fails"); - ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Number of FD in set unchanged"); - - ok(lttng_poll_del(&poll_events, -1) != 0, "Removing from negative FD fails"); - ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Number of FD in set unchanged"); - - ok(lttng_poll_del(&poll_events, 2) == 0, "Removing invalid FD still succeeds"); - ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Number of elements unchanged"); - - ok(lttng_poll_del(&poll_events, 1) == 0, "Removing valid FD succeeds"); - ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Nb of elements decremented"); - - ok(lttng_poll_del(&poll_events, 1) != 0, "Removing from empty set fails"); - ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Nb of elements unchanged"); - - lttng_poll_clean(&poll_events); -} - -static void test_mod_wait(void) -{ - struct lttng_poll_event poll_events; - struct lttng_poll_event cpoll_events; - int hupfd[2]; - int infd[2]; - pid_t cpid; - char rbuf = 0, tbuf = MAGIC_VALUE; - int wstatus; - - lttng_poll_init(&poll_events); - lttng_poll_init(&cpoll_events); - - ok(pipe(hupfd) != -1, "pipe function succeeds"); - ok(pipe(infd) != -1, "pipe function succeeds"); - - cpid = fork(); - if (cpid == 0) { - childok(lttng_poll_create(&cpoll_events, 1, 0) == 0, "Create valid poll set succeeds"); - childok(lttng_poll_mod(NULL, infd[0], LPOLLIN) == -1, "lttng_poll_mod with invalid input returns an error"); - childok(lttng_poll_mod(&cpoll_events, infd[0], LPOLLIN) == -1, "lttng_poll_mod with invalid input returns an error"); - childok(lttng_poll_add(&cpoll_events, infd[0], LPOLLHUP) == 0, "Add valid FD succeeds"); - childok(lttng_poll_mod(&cpoll_events, -1, LPOLLIN) == -1, "lttng_poll_mod with invalid input returns an error"); - childok(lttng_poll_mod(&cpoll_events, hupfd[0], LPOLLIN) == 0, "lttng_poll_mod on unincluded FD goes on"); - childok(lttng_poll_mod(&cpoll_events, infd[0], LPOLLIN) == 0, "Modify event type succeeds"); - childok(close(infd[1]) == 0, "Close valid FD succeeds"); - childok(lttng_poll_wait(&cpoll_events, -1) == 1, "Wait on close times out"); - childok(lttng_read(infd[0], &rbuf, 1) == 1, "Data is present in the pipe"); - childok(rbuf == MAGIC_VALUE, "Received data is consistent with transmitted data"); - childok(lttng_poll_del(&cpoll_events, infd[0]) == 0, "Removing valid FD succeeds"); - childok(close(infd[0]) == 0, "Close valid FD succeeds"); - childok(close(hupfd[0]) == 0, "Close valid FD succeeds"); - childok(close(hupfd[1]) == 0, "Close valid FD succeeds"); - lttng_poll_clean(&cpoll_events); - _exit(EXIT_SUCCESS); - } else { - ok(close(hupfd[1]) == 0, "Close valid FD succeeds"); - ok(close(infd[0]) == 0, "Close valid FD succeeds"); - - ok(lttng_poll_wait(NULL, -1) == -1, "lttng_poll_wait call with invalid input returns error"); - - ok(lttng_poll_create(&poll_events, 1, 0) == 0, "Create valid poll set succeeds"); - ok(lttng_poll_wait(&poll_events, -1) == -1, "lttng_poll_wait call with invalid input returns error"); - ok(lttng_poll_add(&poll_events, hupfd[0], LPOLLHUP) == 0, "Add valid FD succeeds"); - ok(lttng_write(infd[1], &tbuf, 1) == 1, "Write to pipe succeeds"); - ok(lttng_poll_wait(&poll_events, -1) == 1, "Wakes up on one event"); - ok(lttng_poll_del(&poll_events, hupfd[0]) == 0, "Removing valid FD succeeds"); - ok(close(hupfd[0]) == 0, "Close valid FD succeeds"); - ok(close(infd[1]) == 0, "Close valid FD succeeds"); - lttng_poll_clean(&poll_events); - ok(waitpid(cpid, &wstatus, 0) == cpid, "Wait for child exit"); - ok(WIFEXITED(wstatus) == 1, "Child process exited"); - ok(WEXITSTATUS(wstatus) == EXIT_SUCCESS, "Child process exited with EXIT_SUCCESS"); - } -} - -static void destroy_pipe(void *pipe) -{ - lttng_pipe_destroy(pipe); -} - -static int run_active_set_combination(unsigned int fd_count, - unsigned int active_fds_mask) -{ - int ret = 0; - unsigned int i; - const unsigned int active_fds_count = __builtin_popcount(active_fds_mask); - struct lttng_poll_event poll_events; - struct lttng_dynamic_pointer_array pipes; - struct lttng_pipe *pipe = NULL; - - lttng_poll_init(&poll_events); - lttng_dynamic_pointer_array_init(&pipes, destroy_pipe); - - ret = lttng_poll_create(&poll_events, fd_count, 0); - if (ret) { - diag("Failed to create poll set for %u file descriptors", - fd_count); - goto end; - } - - for (i = 0; i < fd_count; i++) { - pipe = lttng_pipe_open(0); - - if (!pipe) { - diag("Failed to allocate pipe"); - ret = -1; - goto end; - } - - ret = lttng_poll_add(&poll_events, lttng_pipe_get_readfd(pipe), - LPOLLIN); - if (ret) { - diag("Failed to add file descriptor to poll set"); - ret = -1; - goto end; - } - - ret = lttng_dynamic_pointer_array_add_pointer(&pipes, pipe); - if (ret) { - diag("Failed to add pipe to pipes array"); - ret = -1; - goto end; - } - - /* Ownership transferred to the pointer array. */ - pipe = NULL; - } - - /* Write one byte for all active fds that should be active. */ - for (i = 0; i < fd_count; i++) { - struct lttng_pipe *borrowed_pipe; - - /* Should this fd be made active? */ - if (!(active_fds_mask & (1 << i))) { - continue; - } - - borrowed_pipe = lttng_dynamic_pointer_array_get_pointer( - &pipes, i); - - ret = lttng_pipe_write( - borrowed_pipe, &(char){'a'}, sizeof(char)); - if (ret != sizeof(char)) { - diag("Failed to write to pipe"); - ret = -1; - goto end; - } - } - - ret = lttng_poll_wait(&poll_events, 0); - if (ret != active_fds_count) { - diag("lttng_poll_wait returned %d, expected %u active file descriptors", - ret, active_fds_count); - ret = -1; - goto end; - } else { - /* Success! */ - ret = 0; - } - -end: - lttng_dynamic_pointer_array_reset(&pipes); - lttng_poll_clean(&poll_events); - lttng_pipe_destroy(pipe); - return ret; -} - -static void test_active_set_combinations(unsigned int fd_count) -{ - unsigned int i, all_active_mask = 0; - - /* Do you really want to test more than 4,294,967,295 combinations? */ - LTTNG_ASSERT(fd_count <= 32); - - for (i = 0; i < fd_count; i++) { - all_active_mask |= (1 << i); - } - - for (i = 0; i <= all_active_mask; i++) { - const int ret = run_active_set_combination(fd_count, i); - - if (ret) { - goto fail; - } - } - - pass("Test all combinations of active file descriptors for %u file descriptors", fd_count); - return; -fail: - fail("Test all combinations of active file descriptors for %u file descriptors", fd_count); -} - -static void test_func_def(void) -{ -#ifdef LTTNG_POLL_GETFD -#define PASS_GETFD 1 -#else -#define PASS_GETFD 0 -#endif - -#ifdef LTTNG_POLL_GETEV -#define PASS_GETEV 1 -#else -#define PASS_GETEV 0 -#endif - -#ifdef LTTNG_POLL_GETSZ -#define PASS_GETSZ 1 -#else -#define PASS_GETSZ 0 -#endif - -#ifdef LTTNG_POLL_GET_PREV_FD -#define PASS_GET_PREV_FD 1 -#else -#define PASS_GET_PREV_FD 0 -#endif - - ok(lttng_poll_reset == lttng_poll_reset, "lttng_poll_reset is defined"); - ok(lttng_poll_init == lttng_poll_init , "lttng_poll_init is defined"); - ok(PASS_GETFD, "GETFD is defined"); - ok(PASS_GETEV, "GETEV is defined"); - ok(PASS_GETSZ, "GETSZ is defined"); - ok(PASS_GET_PREV_FD, "GET_PREV_FD is defined"); -} - -int main(void) -{ - plan_tests(NUM_TESTS); -#ifdef HAVE_EPOLL - test_epoll_compat(); -#endif - test_func_def(); - test_alloc(); - test_add_del(); - test_mod_wait(); - test_active_set_combinations(8); - return exit_status(); -} diff --git a/tests/unit/test_utils_compat_poll.cpp b/tests/unit/test_utils_compat_poll.cpp new file mode 100644 index 000000000..eb6d94a5e --- /dev/null +++ b/tests/unit/test_utils_compat_poll.cpp @@ -0,0 +1,350 @@ +/* + * test_utils_compat_poll.c + * + * Unit tests for the compatibility layer of poll/epoll API. + * + * Copyright (C) 2019 Yannick Lamarre + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* Verification without trashing test order in the child process */ +#define childok(e, test, ...) do { \ + if (!(e)) { \ + diag(test, ## __VA_ARGS__); \ + _exit(EXIT_FAILURE); \ + } \ +} while(0) + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +/* + * Non-zero 8-bits arbitrary value below 0x7f to ensure no sign extension + * occurs. Used to verify that the value is properly propagated through the + * pipe. + */ +#define MAGIC_VALUE ((char) 0x5A) + +#ifdef HAVE_EPOLL +#define NUM_TESTS 48 +#else +#define NUM_TESTS 47 +#endif + +#ifdef HAVE_EPOLL +#if defined(HAVE_EPOLL_CREATE1) && defined(EPOLL_CLOEXEC) +#define CLOE_VALUE EPOLL_CLOEXEC +#else +#define CLOE_VALUE FD_CLOEXEC +#endif + +static +void test_epoll_compat(void) +{ + /* + * Type conversion present to disable warning of anonymous enum from + * compiler. + */ + ok((int) LTTNG_CLOEXEC == (int) CLOE_VALUE, "epoll's CLOEXEC value"); +} +#endif + +static void test_alloc(void) +{ + struct lttng_poll_event poll_events; + + lttng_poll_init(&poll_events); + + /* Null pointer */ + ok(lttng_poll_create(NULL, 1, 0) != 0, "Create over NULL pointer fails"); + /* Size 0 */ + ok(lttng_poll_create(&poll_events, 0, 0) != 0, "Create with size 0 fails"); + /* without CLOEXEC */ + ok(lttng_poll_create(&poll_events, 1, 0) == 0, "Create valid poll set succeeds"); + /* + * lttng_poll_event structure untested due to incompatibility across + * sublayers. lttng_poll_clean cannot be tested. There is no success + * criteria. Verify set's max size cases. + */ + lttng_poll_clean(&poll_events); +} + +/* Tests stuff related to what would be handled with epoll_ctl. */ +static void test_add_del(void) +{ + struct lttng_poll_event poll_events; + + lttng_poll_init(&poll_events); + ok(lttng_poll_add(NULL, 1, LPOLLIN) != 0, "Adding to NULL set fails"); + ok(lttng_poll_add(&poll_events, 1, LPOLLIN) != 0, "Adding to uninitialized structure fails"); + ok(lttng_poll_add(&poll_events, -1, LPOLLIN) != 0, "Adding invalid FD fails"); + + ok(lttng_poll_create(&poll_events, 1, 0) == 0, "Create a poll set succeeds"); + ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Set created empty"); + + ok(lttng_poll_add(NULL, 1, LPOLLIN) != 0, "Adding to NULL set fails"); + ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Set still empty"); + ok(lttng_poll_add(&poll_events, -1, LPOLLIN) != 0, "Adding invalid FD fails"); + ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Set still empty"); + + ok(lttng_poll_add(&poll_events, 1, LPOLLIN) == 0, "Adding valid FD succeeds"); + ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Nb of elements incremented"); + + ok(lttng_poll_del(NULL, 1) != 0, "Removing from NULL set fails"); + ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Number of FD in set unchanged"); + + ok(lttng_poll_del(&poll_events, -1) != 0, "Removing from negative FD fails"); + ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Number of FD in set unchanged"); + + ok(lttng_poll_del(&poll_events, 2) == 0, "Removing invalid FD still succeeds"); + ok(LTTNG_POLL_GETNB(&poll_events) == 1, "Number of elements unchanged"); + + ok(lttng_poll_del(&poll_events, 1) == 0, "Removing valid FD succeeds"); + ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Nb of elements decremented"); + + ok(lttng_poll_del(&poll_events, 1) != 0, "Removing from empty set fails"); + ok(LTTNG_POLL_GETNB(&poll_events) == 0, "Nb of elements unchanged"); + + lttng_poll_clean(&poll_events); +} + +static void test_mod_wait(void) +{ + struct lttng_poll_event poll_events; + struct lttng_poll_event cpoll_events; + int hupfd[2]; + int infd[2]; + pid_t cpid; + char rbuf = 0, tbuf = MAGIC_VALUE; + int wstatus; + + lttng_poll_init(&poll_events); + lttng_poll_init(&cpoll_events); + + ok(pipe(hupfd) != -1, "pipe function succeeds"); + ok(pipe(infd) != -1, "pipe function succeeds"); + + cpid = fork(); + if (cpid == 0) { + childok(lttng_poll_create(&cpoll_events, 1, 0) == 0, "Create valid poll set succeeds"); + childok(lttng_poll_mod(NULL, infd[0], LPOLLIN) == -1, "lttng_poll_mod with invalid input returns an error"); + childok(lttng_poll_mod(&cpoll_events, infd[0], LPOLLIN) == -1, "lttng_poll_mod with invalid input returns an error"); + childok(lttng_poll_add(&cpoll_events, infd[0], LPOLLHUP) == 0, "Add valid FD succeeds"); + childok(lttng_poll_mod(&cpoll_events, -1, LPOLLIN) == -1, "lttng_poll_mod with invalid input returns an error"); + childok(lttng_poll_mod(&cpoll_events, hupfd[0], LPOLLIN) == 0, "lttng_poll_mod on unincluded FD goes on"); + childok(lttng_poll_mod(&cpoll_events, infd[0], LPOLLIN) == 0, "Modify event type succeeds"); + childok(close(infd[1]) == 0, "Close valid FD succeeds"); + childok(lttng_poll_wait(&cpoll_events, -1) == 1, "Wait on close times out"); + childok(lttng_read(infd[0], &rbuf, 1) == 1, "Data is present in the pipe"); + childok(rbuf == MAGIC_VALUE, "Received data is consistent with transmitted data"); + childok(lttng_poll_del(&cpoll_events, infd[0]) == 0, "Removing valid FD succeeds"); + childok(close(infd[0]) == 0, "Close valid FD succeeds"); + childok(close(hupfd[0]) == 0, "Close valid FD succeeds"); + childok(close(hupfd[1]) == 0, "Close valid FD succeeds"); + lttng_poll_clean(&cpoll_events); + _exit(EXIT_SUCCESS); + } else { + ok(close(hupfd[1]) == 0, "Close valid FD succeeds"); + ok(close(infd[0]) == 0, "Close valid FD succeeds"); + + ok(lttng_poll_wait(NULL, -1) == -1, "lttng_poll_wait call with invalid input returns error"); + + ok(lttng_poll_create(&poll_events, 1, 0) == 0, "Create valid poll set succeeds"); + ok(lttng_poll_wait(&poll_events, -1) == -1, "lttng_poll_wait call with invalid input returns error"); + ok(lttng_poll_add(&poll_events, hupfd[0], LPOLLHUP) == 0, "Add valid FD succeeds"); + ok(lttng_write(infd[1], &tbuf, 1) == 1, "Write to pipe succeeds"); + ok(lttng_poll_wait(&poll_events, -1) == 1, "Wakes up on one event"); + ok(lttng_poll_del(&poll_events, hupfd[0]) == 0, "Removing valid FD succeeds"); + ok(close(hupfd[0]) == 0, "Close valid FD succeeds"); + ok(close(infd[1]) == 0, "Close valid FD succeeds"); + lttng_poll_clean(&poll_events); + ok(waitpid(cpid, &wstatus, 0) == cpid, "Wait for child exit"); + ok(WIFEXITED(wstatus) == 1, "Child process exited"); + ok(WEXITSTATUS(wstatus) == EXIT_SUCCESS, "Child process exited with EXIT_SUCCESS"); + } +} + +static void destroy_pipe(void *pipe) +{ + lttng_pipe_destroy((lttng_pipe *) pipe); +} + +static int run_active_set_combination(unsigned int fd_count, + unsigned int active_fds_mask) +{ + int ret = 0; + unsigned int i; + const unsigned int active_fds_count = __builtin_popcount(active_fds_mask); + struct lttng_poll_event poll_events; + struct lttng_dynamic_pointer_array pipes; + struct lttng_pipe *pipe = NULL; + + lttng_poll_init(&poll_events); + lttng_dynamic_pointer_array_init(&pipes, destroy_pipe); + + ret = lttng_poll_create(&poll_events, fd_count, 0); + if (ret) { + diag("Failed to create poll set for %u file descriptors", + fd_count); + goto end; + } + + for (i = 0; i < fd_count; i++) { + pipe = lttng_pipe_open(0); + + if (!pipe) { + diag("Failed to allocate pipe"); + ret = -1; + goto end; + } + + ret = lttng_poll_add(&poll_events, lttng_pipe_get_readfd(pipe), + LPOLLIN); + if (ret) { + diag("Failed to add file descriptor to poll set"); + ret = -1; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer(&pipes, pipe); + if (ret) { + diag("Failed to add pipe to pipes array"); + ret = -1; + goto end; + } + + /* Ownership transferred to the pointer array. */ + pipe = NULL; + } + + /* Write one byte for all active fds that should be active. */ + for (i = 0; i < fd_count; i++) { + struct lttng_pipe *borrowed_pipe; + + /* Should this fd be made active? */ + if (!(active_fds_mask & (1 << i))) { + continue; + } + + borrowed_pipe = + (lttng_pipe *) lttng_dynamic_pointer_array_get_pointer( + &pipes, i); + + char c = 'a'; + ret = lttng_pipe_write( + borrowed_pipe, &c, sizeof(char)); + if (ret != sizeof(char)) { + diag("Failed to write to pipe"); + ret = -1; + goto end; + } + } + + ret = lttng_poll_wait(&poll_events, 0); + if (ret != active_fds_count) { + diag("lttng_poll_wait returned %d, expected %u active file descriptors", + ret, active_fds_count); + ret = -1; + goto end; + } else { + /* Success! */ + ret = 0; + } + +end: + lttng_dynamic_pointer_array_reset(&pipes); + lttng_poll_clean(&poll_events); + lttng_pipe_destroy(pipe); + return ret; +} + +static void test_active_set_combinations(unsigned int fd_count) +{ + unsigned int i, all_active_mask = 0; + + /* Do you really want to test more than 4,294,967,295 combinations? */ + LTTNG_ASSERT(fd_count <= 32); + + for (i = 0; i < fd_count; i++) { + all_active_mask |= (1 << i); + } + + for (i = 0; i <= all_active_mask; i++) { + const int ret = run_active_set_combination(fd_count, i); + + if (ret) { + goto fail; + } + } + + pass("Test all combinations of active file descriptors for %u file descriptors", fd_count); + return; +fail: + fail("Test all combinations of active file descriptors for %u file descriptors", fd_count); +} + +static void test_func_def(void) +{ +#ifdef LTTNG_POLL_GETFD +#define PASS_GETFD 1 +#else +#define PASS_GETFD 0 +#endif + +#ifdef LTTNG_POLL_GETEV +#define PASS_GETEV 1 +#else +#define PASS_GETEV 0 +#endif + +#ifdef LTTNG_POLL_GETSZ +#define PASS_GETSZ 1 +#else +#define PASS_GETSZ 0 +#endif + +#ifdef LTTNG_POLL_GET_PREV_FD +#define PASS_GET_PREV_FD 1 +#else +#define PASS_GET_PREV_FD 0 +#endif + + ok(lttng_poll_reset == lttng_poll_reset, "lttng_poll_reset is defined"); + ok(lttng_poll_init == lttng_poll_init , "lttng_poll_init is defined"); + ok(PASS_GETFD, "GETFD is defined"); + ok(PASS_GETEV, "GETEV is defined"); + ok(PASS_GETSZ, "GETSZ is defined"); + ok(PASS_GET_PREV_FD, "GET_PREV_FD is defined"); +} + +int main(void) +{ + plan_tests(NUM_TESTS); +#ifdef HAVE_EPOLL + test_epoll_compat(); +#endif + test_func_def(); + test_alloc(); + test_add_del(); + test_mod_wait(); + test_active_set_combinations(8); + return exit_status(); +} diff --git a/tests/unit/test_utils_compat_pthread.c b/tests/unit/test_utils_compat_pthread.c deleted file mode 100644 index eb2b7b6ec..000000000 --- a/tests/unit/test_utils_compat_pthread.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2020 Michael Jeanson - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include "common/compat/pthread.h" - -#include - -#define TEST_NAME_PROPER_LEN 16 - -int main(int argc, char **argv) -{ - int ret; - char name1[TEST_NAME_PROPER_LEN]; - char name2[TEST_NAME_PROPER_LEN]; - char too_long_name[] = "thisnameistoolong"; - char short_name[] = "labatt50"; - char long_name[] = "procrastinating"; - - plan_tests(10); - - /* Get the initial thread name */ - ret = lttng_pthread_getname_np(name1, TEST_NAME_PROPER_LEN); - ok(ret == 0, "Get the thread name: '%s'", name1); - - /* Set a thread name of more than 16 bytes, should fail */ - ret = lttng_pthread_setname_np(too_long_name); - ok(ret == ERANGE, "Set a too long thread name: '%s'", too_long_name); - - /* Get the thread name again, shouldn't have changed */ - ret = lttng_pthread_getname_np(name2, TEST_NAME_PROPER_LEN); - ok(ret == 0, "Get the thread name: '%s'", name2); - ok(strcmp(name1, name2) == 0, "Compare the initial thread name: '%s' == '%s'", name1, name2); - - /* Set a thread name of less than 16 bytes */ - ret = lttng_pthread_setname_np(short_name); - ok(ret == 0, "Set a short thread name: '%s'", short_name); - - /* Get the thread name again, should be the one we set */ - ret = lttng_pthread_getname_np(name1, TEST_NAME_PROPER_LEN); - ok(ret == 0, "Get a short thread name: '%s'", name1); - ok(strcmp(short_name, name1) == 0, "Compare the short thread name: '%s' == '%s'", short_name, name1); - - - /* Set a thread name of 16 bytes */ - ret = lttng_pthread_setname_np(long_name); - ok(ret == 0, "Set a long thread name: '%s'", long_name); - - /* Get the thread name again, should be the one we set */ - ret = lttng_pthread_getname_np(name1, TEST_NAME_PROPER_LEN); - ok(ret == 0, "Get a long thread name: '%s'", name1); - ok(strncmp(long_name, name1, TEST_NAME_PROPER_LEN - 1) == 0, "Compare the long thread name: '%s' == '%s'", long_name, name1); - - return exit_status(); -} diff --git a/tests/unit/test_utils_compat_pthread.cpp b/tests/unit/test_utils_compat_pthread.cpp new file mode 100644 index 000000000..eb2b7b6ec --- /dev/null +++ b/tests/unit/test_utils_compat_pthread.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 Michael Jeanson + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include "common/compat/pthread.h" + +#include + +#define TEST_NAME_PROPER_LEN 16 + +int main(int argc, char **argv) +{ + int ret; + char name1[TEST_NAME_PROPER_LEN]; + char name2[TEST_NAME_PROPER_LEN]; + char too_long_name[] = "thisnameistoolong"; + char short_name[] = "labatt50"; + char long_name[] = "procrastinating"; + + plan_tests(10); + + /* Get the initial thread name */ + ret = lttng_pthread_getname_np(name1, TEST_NAME_PROPER_LEN); + ok(ret == 0, "Get the thread name: '%s'", name1); + + /* Set a thread name of more than 16 bytes, should fail */ + ret = lttng_pthread_setname_np(too_long_name); + ok(ret == ERANGE, "Set a too long thread name: '%s'", too_long_name); + + /* Get the thread name again, shouldn't have changed */ + ret = lttng_pthread_getname_np(name2, TEST_NAME_PROPER_LEN); + ok(ret == 0, "Get the thread name: '%s'", name2); + ok(strcmp(name1, name2) == 0, "Compare the initial thread name: '%s' == '%s'", name1, name2); + + /* Set a thread name of less than 16 bytes */ + ret = lttng_pthread_setname_np(short_name); + ok(ret == 0, "Set a short thread name: '%s'", short_name); + + /* Get the thread name again, should be the one we set */ + ret = lttng_pthread_getname_np(name1, TEST_NAME_PROPER_LEN); + ok(ret == 0, "Get a short thread name: '%s'", name1); + ok(strcmp(short_name, name1) == 0, "Compare the short thread name: '%s' == '%s'", short_name, name1); + + + /* Set a thread name of 16 bytes */ + ret = lttng_pthread_setname_np(long_name); + ok(ret == 0, "Set a long thread name: '%s'", long_name); + + /* Get the thread name again, should be the one we set */ + ret = lttng_pthread_getname_np(name1, TEST_NAME_PROPER_LEN); + ok(ret == 0, "Get a long thread name: '%s'", name1); + ok(strncmp(long_name, name1, TEST_NAME_PROPER_LEN - 1) == 0, "Compare the long thread name: '%s' == '%s'", long_name, name1); + + return exit_status(); +} diff --git a/tests/unit/test_utils_expand_path.c b/tests/unit/test_utils_expand_path.c deleted file mode 100644 index a55d7cf38..000000000 --- a/tests/unit/test_utils_expand_path.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2013 Raphaël Beamonte - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose = 3; -int lttng_opt_mi; - -struct valid_test_input { - const char *input; - const char *relative_part; - const char *absolute_part; -}; - -struct tree_symlink { - const char *orig; - const char *dest; -}; - -struct symlink_test_input { - const char *input; - const char *expected_result; -}; - -/* Valid test cases */ -static struct valid_test_input valid_tests_inputs[] = { - { "/a/b/c/d/e", "", "/a/b/c/d/e" }, - { "/a//b//c/d/e", "", "/a/b/c/d/e" }, - { "./a/b/c/d/e", ".", "/a/b/c/d/e" }, - { "../a/b/c/d/../e", "..", "/a/b/c/e" }, - { ".././a/b/c/d/./e", "..", "/a/b/c/d/e" }, - { "../../a/b/c/d/e", "../..", "/a/b/c/d/e" }, - { "./a/b/../c/d/../e", ".", "/a/c/e" }, - { "../a/b/../../c/./d/./e", "..", "/c/d/e" }, - { "../../a/b/../c/d/../../e", "../..", "/a/e" }, - { "./a/b/c/d/../../../../e", ".", "/e" }, - { ".././a/b/c/d/./e", "..", "/a/b/c/d/e" }, - { "a/", ".", "/a/" }, - { "a", ".", "/a" }, - { "../../", "../..", "/" }, - { "../..", "../..", "" }, - { "../", "..", "/" }, - { "..", "..", "" }, - { "./", ".", "/" }, - { ".", ".", "" }, - { "/../a/b/c/d/e", "", "/a/b/c/d/e" }, - { "/a/b/c/d/../../../../../e", "", "/e" }, - { "/..", "", "/" }, - { "/a/..", "", "/" }, -}; -char **valid_tests_expected_results; -static const int num_valid_tests = - sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); - -/* Symlinks test cases */ -char tree_origin[] = "/tmp/test_utils_expand_path.XXXXXX"; - -static const char * const tree_dirs[] = { - "a", - "a/b", - "a/b/c", - "a/e", -}; -static const int num_tree_dirs = - sizeof(tree_dirs) / sizeof(tree_dirs[0]); - -static struct tree_symlink tree_symlinks[] = { - { "a/d", "b/c/" }, - { "a/g", "d/" }, - { "a/b/f", "../e/" }, - { "a/b/h", "../g/" }, - { "a/b/k", "c/g/" }, - { "a/b/c/g", "../../../" }, -}; -static const int num_tree_symlinks = - sizeof(tree_symlinks) / sizeof(tree_symlinks[0]); - -static struct symlink_test_input symlink_tests_inputs[] = { - { "a/g/../l/.", "a/b/l" }, - { "a/g/../l/./", "a/b/l/" }, - { "a/g/../l/..", "a/b" }, - { "a/g/../l/../", "a/b/" }, - { "a/b/h/g/", "" }, -}; -static const int num_symlink_tests = - sizeof(symlink_tests_inputs) / sizeof(symlink_tests_inputs[0]); - -/* Invalid test cases */ -static char *invalid_tests_inputs[] = { - NULL, -}; -static const int num_invalid_tests = - sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]); - -#define PRINT_ERR(fmt, args...) \ - fprintf(stderr, "test_utils_expand_path: error: " fmt "\n", ## args) - -static int prepare_valid_results(void) -{ - int i; - char *relative, *cur_path = NULL, *prev_path = NULL, - *pprev_path = NULL, *empty = NULL; - int ret = 0; - - /* Prepare the relative paths */ - cur_path = realpath(".", NULL); - prev_path = realpath("..", NULL); - pprev_path = realpath("../..", NULL); - empty = strdup(""); - if (!cur_path || !prev_path || !pprev_path || !empty) { - PRINT_ERR("strdup out of memory"); - ret = -1; - goto end; - } - - /* allocate memory for the expected results */ - valid_tests_expected_results = zmalloc(sizeof(char *) * num_valid_tests); - if (!valid_tests_expected_results) { - PRINT_ERR("out of memory"); - ret = -1; - goto end; - } - for (i = 0; i < num_valid_tests; i++) { - valid_tests_expected_results[i] = malloc(PATH_MAX); - if (valid_tests_expected_results[i] == NULL) { - PRINT_ERR("malloc expected results"); - ret = -1; - goto end; - } - - if (strcmp(valid_tests_inputs[i].relative_part, ".") == 0) { - relative = cur_path; - } else if (strcmp(valid_tests_inputs[i].relative_part, "..") == 0) { - relative = prev_path; - } else if (strcmp(valid_tests_inputs[i].relative_part, "../..") == 0) { - relative = pprev_path; - } else { - relative = empty; - } - - snprintf(valid_tests_expected_results[i], PATH_MAX, - "%s%s", relative, valid_tests_inputs[i].absolute_part); - } - -end: - free(cur_path); - free(prev_path); - free(pprev_path); - free(empty); - - return ret; -} - -static int free_valid_results(void) -{ - int i; - - for (i = 0; i < num_valid_tests; i++) { - free(valid_tests_expected_results[i]); - } - - free(valid_tests_expected_results); - - return 0; -} - -static int prepare_symlink_tree(void) -{ - int i; - char tmppath[PATH_MAX] = {}; - - /* Create the temporary directory */ - if (mkdtemp(tree_origin) == NULL) { - PRINT_ERR("failed to mkdtemp"); - goto error; - } - - /* Create the directories of the test tree */ - for (i = 0; i < num_tree_dirs; i++) { - snprintf(tmppath, sizeof(tmppath), "%s/%s", tree_origin, - tree_dirs[i]); - - if (mkdir(tmppath, 0755) != 0) { - PRINT_ERR("mkdir failed with path \"%s\"", tmppath); - goto error; - } - } - - /* Create the symlinks of the test tree */ - for (i = 0; i < num_tree_symlinks; i++) { - snprintf(tmppath, sizeof(tmppath), "%s/%s", - tree_origin, tree_symlinks[i].orig); - - if (symlink(tree_symlinks[i].dest, tmppath) != 0) { - PRINT_ERR("failed to symlink \"%s\" to \"%s\"", tmppath, - tree_symlinks[i].dest); - goto error; - } - } - - return 0; - -error: - return 1; -} - -static int free_symlink_tree(void) -{ - int i; - char tmppath[PATH_MAX]; - - /* Remove the symlinks from the test tree */ - for (i = num_tree_symlinks - 1; i > -1; i--) { - snprintf(tmppath, PATH_MAX, "%s/%s", - tree_origin, tree_symlinks[i].orig); - - if (unlink(tmppath) != 0) { - PRINT_ERR("failed to unlink \"%s\"", tmppath); - goto error; - } - } - - /* Remove the directories from the test tree */ - for (i = num_tree_dirs - 1; i > -1; i--) { - snprintf(tmppath, PATH_MAX, "%s/%s", tree_origin, tree_dirs[i]); - - if (rmdir(tmppath) != 0) { - PRINT_ERR("failed to rmdir \"%s\"", tmppath); - goto error; - } - } - - /* Remove the temporary directory */ - if (rmdir(tree_origin) != 0) { - PRINT_ERR("failed to rmdir \"%s\"", tree_origin); - goto error; - } - - return 0; - -error: - return 1; -} - -static void test_utils_expand_path(void) -{ - char *result; - char name[100], tmppath[PATH_MAX], real_tree_origin[PATH_MAX]; - int i, treelen; - - /* Test valid cases */ - for (i = 0; i < num_valid_tests; i++) { - sprintf(name, "valid test case: %s", valid_tests_inputs[i].input); - - result = utils_expand_path(valid_tests_inputs[i].input); - ok(result != NULL && - strcmp(result, valid_tests_expected_results[i]) == 0, name); - - free(result); - } - - /* - * Get the realpath for the tree_origin since it can itself be a - * symlink. - */ - result = realpath(tree_origin, real_tree_origin); - if (!result) { - fail("realpath failed."); - return; - } - - /* Test symlink tree cases */ - treelen = strlen(real_tree_origin) + 1; - for (i = 0; i < num_symlink_tests; i++) { - int ret; - - sprintf(name, "symlink tree test case: [tmppath/]%s", - symlink_tests_inputs[i].input); - - ret = snprintf(tmppath, PATH_MAX, "%s/%s", - real_tree_origin, - symlink_tests_inputs[i].input); - if (ret == -1 || ret >= PATH_MAX) { - PRINT_ERR("truncation occurred while concatenating paths \"%s\" and \"%s\"", - real_tree_origin, - symlink_tests_inputs[i].input); - fail(name); - continue; - } - result = utils_expand_path(tmppath); - ok(result != NULL && strcmp(result + treelen, - symlink_tests_inputs[i].expected_result) == 0, name); - - free(result); - } - - /* Test invalid cases */ - for (i = 0; i < num_invalid_tests; i++) { - const char *test_input = invalid_tests_inputs[i]; - - sprintf(name, "invalid test case: %s", test_input ? - test_input : "NULL"); - - result = utils_expand_path(test_input); - if (result != NULL) { - free(result); - } - ok(result == NULL, name); - } -} - -int main(int argc, char **argv) -{ - if (prepare_symlink_tree() != 0) { - goto error_mkdir; - } - - if (prepare_valid_results() != 0) { - goto error_malloc; - } - - plan_tests(num_valid_tests + num_invalid_tests + num_symlink_tests); - - diag("utils_expand_path tests"); - - test_utils_expand_path(); - - free_valid_results(); - free_symlink_tree(); - - return exit_status(); - -error_malloc: - free_valid_results(); - -error_mkdir: - free_symlink_tree(); - - return 1; -} diff --git a/tests/unit/test_utils_expand_path.cpp b/tests/unit/test_utils_expand_path.cpp new file mode 100644 index 000000000..428acad22 --- /dev/null +++ b/tests/unit/test_utils_expand_path.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2013 Raphaël Beamonte + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 3; +int lttng_opt_mi; + +struct valid_test_input { + const char *input; + const char *relative_part; + const char *absolute_part; +}; + +struct tree_symlink { + const char *orig; + const char *dest; +}; + +struct symlink_test_input { + const char *input; + const char *expected_result; +}; + +/* Valid test cases */ +static struct valid_test_input valid_tests_inputs[] = { + { "/a/b/c/d/e", "", "/a/b/c/d/e" }, + { "/a//b//c/d/e", "", "/a/b/c/d/e" }, + { "./a/b/c/d/e", ".", "/a/b/c/d/e" }, + { "../a/b/c/d/../e", "..", "/a/b/c/e" }, + { ".././a/b/c/d/./e", "..", "/a/b/c/d/e" }, + { "../../a/b/c/d/e", "../..", "/a/b/c/d/e" }, + { "./a/b/../c/d/../e", ".", "/a/c/e" }, + { "../a/b/../../c/./d/./e", "..", "/c/d/e" }, + { "../../a/b/../c/d/../../e", "../..", "/a/e" }, + { "./a/b/c/d/../../../../e", ".", "/e" }, + { ".././a/b/c/d/./e", "..", "/a/b/c/d/e" }, + { "a/", ".", "/a/" }, + { "a", ".", "/a" }, + { "../../", "../..", "/" }, + { "../..", "../..", "" }, + { "../", "..", "/" }, + { "..", "..", "" }, + { "./", ".", "/" }, + { ".", ".", "" }, + { "/../a/b/c/d/e", "", "/a/b/c/d/e" }, + { "/a/b/c/d/../../../../../e", "", "/e" }, + { "/..", "", "/" }, + { "/a/..", "", "/" }, +}; +char **valid_tests_expected_results; +static const int num_valid_tests = + sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); + +/* Symlinks test cases */ +char tree_origin[] = "/tmp/test_utils_expand_path.XXXXXX"; + +static const char * const tree_dirs[] = { + "a", + "a/b", + "a/b/c", + "a/e", +}; +static const int num_tree_dirs = + sizeof(tree_dirs) / sizeof(tree_dirs[0]); + +static struct tree_symlink tree_symlinks[] = { + { "a/d", "b/c/" }, + { "a/g", "d/" }, + { "a/b/f", "../e/" }, + { "a/b/h", "../g/" }, + { "a/b/k", "c/g/" }, + { "a/b/c/g", "../../../" }, +}; +static const int num_tree_symlinks = + sizeof(tree_symlinks) / sizeof(tree_symlinks[0]); + +static struct symlink_test_input symlink_tests_inputs[] = { + { "a/g/../l/.", "a/b/l" }, + { "a/g/../l/./", "a/b/l/" }, + { "a/g/../l/..", "a/b" }, + { "a/g/../l/../", "a/b/" }, + { "a/b/h/g/", "" }, +}; +static const int num_symlink_tests = + sizeof(symlink_tests_inputs) / sizeof(symlink_tests_inputs[0]); + +/* Invalid test cases */ +static char *invalid_tests_inputs[] = { + NULL, +}; +static const int num_invalid_tests = + sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]); + +#define PRINT_ERR(fmt, args...) \ + fprintf(stderr, "test_utils_expand_path: error: " fmt "\n", ## args) + +static int prepare_valid_results(void) +{ + int i; + char *relative, *cur_path = NULL, *prev_path = NULL, + *pprev_path = NULL, *empty = NULL; + int ret = 0; + + /* Prepare the relative paths */ + cur_path = realpath(".", NULL); + prev_path = realpath("..", NULL); + pprev_path = realpath("../..", NULL); + empty = strdup(""); + if (!cur_path || !prev_path || !pprev_path || !empty) { + PRINT_ERR("strdup out of memory"); + ret = -1; + goto end; + } + + /* allocate memory for the expected results */ + valid_tests_expected_results = (char **) zmalloc(sizeof(char *) * num_valid_tests); + if (!valid_tests_expected_results) { + PRINT_ERR("out of memory"); + ret = -1; + goto end; + } + for (i = 0; i < num_valid_tests; i++) { + valid_tests_expected_results[i] = (char *) malloc(PATH_MAX); + if (valid_tests_expected_results[i] == NULL) { + PRINT_ERR("malloc expected results"); + ret = -1; + goto end; + } + + if (strcmp(valid_tests_inputs[i].relative_part, ".") == 0) { + relative = cur_path; + } else if (strcmp(valid_tests_inputs[i].relative_part, "..") == 0) { + relative = prev_path; + } else if (strcmp(valid_tests_inputs[i].relative_part, "../..") == 0) { + relative = pprev_path; + } else { + relative = empty; + } + + snprintf(valid_tests_expected_results[i], PATH_MAX, + "%s%s", relative, valid_tests_inputs[i].absolute_part); + } + +end: + free(cur_path); + free(prev_path); + free(pprev_path); + free(empty); + + return ret; +} + +static int free_valid_results(void) +{ + int i; + + for (i = 0; i < num_valid_tests; i++) { + free(valid_tests_expected_results[i]); + } + + free(valid_tests_expected_results); + + return 0; +} + +static int prepare_symlink_tree(void) +{ + int i; + char tmppath[PATH_MAX] = {}; + + /* Create the temporary directory */ + if (mkdtemp(tree_origin) == NULL) { + PRINT_ERR("failed to mkdtemp"); + goto error; + } + + /* Create the directories of the test tree */ + for (i = 0; i < num_tree_dirs; i++) { + snprintf(tmppath, sizeof(tmppath), "%s/%s", tree_origin, + tree_dirs[i]); + + if (mkdir(tmppath, 0755) != 0) { + PRINT_ERR("mkdir failed with path \"%s\"", tmppath); + goto error; + } + } + + /* Create the symlinks of the test tree */ + for (i = 0; i < num_tree_symlinks; i++) { + snprintf(tmppath, sizeof(tmppath), "%s/%s", + tree_origin, tree_symlinks[i].orig); + + if (symlink(tree_symlinks[i].dest, tmppath) != 0) { + PRINT_ERR("failed to symlink \"%s\" to \"%s\"", tmppath, + tree_symlinks[i].dest); + goto error; + } + } + + return 0; + +error: + return 1; +} + +static int free_symlink_tree(void) +{ + int i; + char tmppath[PATH_MAX]; + + /* Remove the symlinks from the test tree */ + for (i = num_tree_symlinks - 1; i > -1; i--) { + snprintf(tmppath, PATH_MAX, "%s/%s", + tree_origin, tree_symlinks[i].orig); + + if (unlink(tmppath) != 0) { + PRINT_ERR("failed to unlink \"%s\"", tmppath); + goto error; + } + } + + /* Remove the directories from the test tree */ + for (i = num_tree_dirs - 1; i > -1; i--) { + snprintf(tmppath, PATH_MAX, "%s/%s", tree_origin, tree_dirs[i]); + + if (rmdir(tmppath) != 0) { + PRINT_ERR("failed to rmdir \"%s\"", tmppath); + goto error; + } + } + + /* Remove the temporary directory */ + if (rmdir(tree_origin) != 0) { + PRINT_ERR("failed to rmdir \"%s\"", tree_origin); + goto error; + } + + return 0; + +error: + return 1; +} + +static void test_utils_expand_path(void) +{ + char *result; + char name[100], tmppath[PATH_MAX], real_tree_origin[PATH_MAX]; + int i, treelen; + + /* Test valid cases */ + for (i = 0; i < num_valid_tests; i++) { + sprintf(name, "valid test case: %s", valid_tests_inputs[i].input); + + result = utils_expand_path(valid_tests_inputs[i].input); + ok(result != NULL && + strcmp(result, valid_tests_expected_results[i]) == 0, name); + + free(result); + } + + /* + * Get the realpath for the tree_origin since it can itself be a + * symlink. + */ + result = realpath(tree_origin, real_tree_origin); + if (!result) { + fail("realpath failed."); + return; + } + + /* Test symlink tree cases */ + treelen = strlen(real_tree_origin) + 1; + for (i = 0; i < num_symlink_tests; i++) { + int ret; + + sprintf(name, "symlink tree test case: [tmppath/]%s", + symlink_tests_inputs[i].input); + + ret = snprintf(tmppath, PATH_MAX, "%s/%s", + real_tree_origin, + symlink_tests_inputs[i].input); + if (ret == -1 || ret >= PATH_MAX) { + PRINT_ERR("truncation occurred while concatenating paths \"%s\" and \"%s\"", + real_tree_origin, + symlink_tests_inputs[i].input); + fail(name); + continue; + } + result = utils_expand_path(tmppath); + ok(result != NULL && strcmp(result + treelen, + symlink_tests_inputs[i].expected_result) == 0, name); + + free(result); + } + + /* Test invalid cases */ + for (i = 0; i < num_invalid_tests; i++) { + const char *test_input = invalid_tests_inputs[i]; + + sprintf(name, "invalid test case: %s", test_input ? + test_input : "NULL"); + + result = utils_expand_path(test_input); + if (result != NULL) { + free(result); + } + ok(result == NULL, name); + } +} + +int main(int argc, char **argv) +{ + if (prepare_symlink_tree() != 0) { + goto error_mkdir; + } + + if (prepare_valid_results() != 0) { + goto error_malloc; + } + + plan_tests(num_valid_tests + num_invalid_tests + num_symlink_tests); + + diag("utils_expand_path tests"); + + test_utils_expand_path(); + + free_valid_results(); + free_symlink_tree(); + + return exit_status(); + +error_malloc: + free_valid_results(); + +error_mkdir: + free_symlink_tree(); + + return 1; +} diff --git a/tests/unit/test_utils_parse_size_suffix.c b/tests/unit/test_utils_parse_size_suffix.c deleted file mode 100644 index 04f2fffe1..000000000 --- a/tests/unit/test_utils_parse_size_suffix.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2013 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include - -#include - -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose = 3; -int lttng_opt_mi; - -struct valid_test_input { - const char *input; - uint64_t expected_result; -}; - -/* Valid test cases */ -static struct valid_test_input valid_tests_inputs[] = { - { "0", 0 }, - { "1234", 1234 }, - { "0x400", 1024 }, - { "0300", 192 }, - { "16k", 16384 }, - { "128K", 131072 }, - { "0x1234k", 4771840 }, - { "32M", 33554432 }, - { "1024G", 1099511627776ULL }, - { "0X400", 1024 }, - { "0x40a", 1034 }, - { "0X40b", 1035 }, - { "0x40C", 1036 }, - { "0X40D", 1037 }, - { "0x40e", 1038 }, - { "0X40f", 1039 }, - { "00", 0 }, - { "0k", 0 }, - { "0K", 0 }, - { "0M", 0 }, - { "0G", 0 }, - { "00k", 0 }, - { "00K", 0 }, - { "00M", 0 }, - { "00G", 0 }, - { "0x0", 0 }, - { "0X0", 0 }, - { "0x0k", 0 }, - { "0X0K", 0 }, - { "0x0M", 0 }, - { "0X0G", 0 }, - { "0X40G", 68719476736ULL }, - { "0300k", 196608 }, - { "0300K", 196608 }, - { "030M", 25165824 }, - { "020G", 17179869184ULL }, - { "0xa0k", 163840 }, - { "0xa0K", 163840 }, - { "0XA0M", 167772160 }, - { "0xA0G", 171798691840ULL }, -}; -static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); - -/* Invalid test cases */ -static const char *invalid_tests_inputs[] = { - "", - " ", - "-1", - "k", - "4611686018427387904G", - "0x40g", - "08", - "09", - "0x", - "x0", - "0xx0", - "07kK", - "0xk", - "0XM", - "0xG", - "0x0MM", - "0X0GG", - "0a", - "0B", -}; - -static const int num_invalid_tests = sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]); - -static void test_utils_parse_size_suffix(void) -{ - uint64_t result; - int ret; - int i; - - /* Test valid cases */ - for (i = 0; i < num_valid_tests; i++) { - char name[100]; - sprintf(name, "valid test case: %s", valid_tests_inputs[i].input); - - ret = utils_parse_size_suffix(valid_tests_inputs[i].input, &result); - ok(ret == 0 && result == valid_tests_inputs[i].expected_result, name); - } - - /* Test invalid cases */ - for (i = 0; i < num_invalid_tests; i++) { - char name[100]; - sprintf(name, "invalid test case: %s", invalid_tests_inputs[i]); - - ret = utils_parse_size_suffix(invalid_tests_inputs[i], &result); - ok(ret != 0, name); - } -} - -int main(int argc, char **argv) -{ - plan_tests(num_valid_tests + num_invalid_tests); - - diag("utils_parse_size_suffix tests"); - - test_utils_parse_size_suffix(); - - return exit_status(); -} diff --git a/tests/unit/test_utils_parse_size_suffix.cpp b/tests/unit/test_utils_parse_size_suffix.cpp new file mode 100644 index 000000000..04f2fffe1 --- /dev/null +++ b/tests/unit/test_utils_parse_size_suffix.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2013 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include + +#include + +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 3; +int lttng_opt_mi; + +struct valid_test_input { + const char *input; + uint64_t expected_result; +}; + +/* Valid test cases */ +static struct valid_test_input valid_tests_inputs[] = { + { "0", 0 }, + { "1234", 1234 }, + { "0x400", 1024 }, + { "0300", 192 }, + { "16k", 16384 }, + { "128K", 131072 }, + { "0x1234k", 4771840 }, + { "32M", 33554432 }, + { "1024G", 1099511627776ULL }, + { "0X400", 1024 }, + { "0x40a", 1034 }, + { "0X40b", 1035 }, + { "0x40C", 1036 }, + { "0X40D", 1037 }, + { "0x40e", 1038 }, + { "0X40f", 1039 }, + { "00", 0 }, + { "0k", 0 }, + { "0K", 0 }, + { "0M", 0 }, + { "0G", 0 }, + { "00k", 0 }, + { "00K", 0 }, + { "00M", 0 }, + { "00G", 0 }, + { "0x0", 0 }, + { "0X0", 0 }, + { "0x0k", 0 }, + { "0X0K", 0 }, + { "0x0M", 0 }, + { "0X0G", 0 }, + { "0X40G", 68719476736ULL }, + { "0300k", 196608 }, + { "0300K", 196608 }, + { "030M", 25165824 }, + { "020G", 17179869184ULL }, + { "0xa0k", 163840 }, + { "0xa0K", 163840 }, + { "0XA0M", 167772160 }, + { "0xA0G", 171798691840ULL }, +}; +static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); + +/* Invalid test cases */ +static const char *invalid_tests_inputs[] = { + "", + " ", + "-1", + "k", + "4611686018427387904G", + "0x40g", + "08", + "09", + "0x", + "x0", + "0xx0", + "07kK", + "0xk", + "0XM", + "0xG", + "0x0MM", + "0X0GG", + "0a", + "0B", +}; + +static const int num_invalid_tests = sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]); + +static void test_utils_parse_size_suffix(void) +{ + uint64_t result; + int ret; + int i; + + /* Test valid cases */ + for (i = 0; i < num_valid_tests; i++) { + char name[100]; + sprintf(name, "valid test case: %s", valid_tests_inputs[i].input); + + ret = utils_parse_size_suffix(valid_tests_inputs[i].input, &result); + ok(ret == 0 && result == valid_tests_inputs[i].expected_result, name); + } + + /* Test invalid cases */ + for (i = 0; i < num_invalid_tests; i++) { + char name[100]; + sprintf(name, "invalid test case: %s", invalid_tests_inputs[i]); + + ret = utils_parse_size_suffix(invalid_tests_inputs[i], &result); + ok(ret != 0, name); + } +} + +int main(int argc, char **argv) +{ + plan_tests(num_valid_tests + num_invalid_tests); + + diag("utils_parse_size_suffix tests"); + + test_utils_parse_size_suffix(); + + return exit_status(); +} diff --git a/tests/unit/test_utils_parse_time_suffix.c b/tests/unit/test_utils_parse_time_suffix.c deleted file mode 100644 index 95aac5561..000000000 --- a/tests/unit/test_utils_parse_time_suffix.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2015 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include - -#include - -#include - -/* For error.h */ -int lttng_opt_quiet = 1; -int lttng_opt_verbose = 3; -int lttng_opt_mi; - -struct valid_test_input { - const char *input; - uint64_t expected_result; -}; - -/* Valid test cases */ -static struct valid_test_input valid_tests_inputs[] = { - { "0", 0 }, - { "1234", 1234 }, - { "1234us", 1234 }, - { "16ms", 16000 }, - { "128ms", 128000 }, - { "32s", 32000000 }, - { "1m", 60000000 }, - { "20m", 1200000000 }, - { "1h", 3600000000 }, - { "5h", 18000000000 }, - { "00", 0 }, - { "0us", 0 }, - { "0ms", 0 }, - { "0s", 0 }, - { "0m", 0 }, - { "0h", 0 }, - { "00us", 0 }, - { "00ms", 0 }, - { "00s", 0 }, - { "00m", 0 }, - { "00h", 0 }, - { "12ms", 12000 }, - { "3597us", 3597 }, - { "+5", 5 }, - { "08", 8 }, - { "0145us", 145 }, -}; -static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); - -/* Invalid test cases */ -static const char *invalid_tests_inputs[] = { - "", - " ", - "-1", - "m", - "4611686018427387904s", - "0x40M", - "0x", - "x0", - "0xx0", - "07mm", - "0xm", - "0Xs", - "0x0ss", - "0a", - "0B", - "0x3 s", - "0xbs ", - "14ns", - "0xbs", - "14ns", - "14ms garbage after value", - "0x14s", - "0u", - "5mS", - "5Ms", - "12ussr", - "67msrp", - "14si", - "12mo", - "53hi", -}; -static const int num_invalid_tests = sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]); - -static void test_utils_parse_time_suffix(void) -{ - uint64_t result; - int ret; - int i; - - /* Test valid cases */ - for (i = 0; i < num_valid_tests; i++) { - char name[256]; - - ret = utils_parse_time_suffix(valid_tests_inputs[i].input, &result); - sprintf(name, "valid test case: %s expected %" PRIu64, valid_tests_inputs[i].input, result); - ok(ret == 0 && result == valid_tests_inputs[i].expected_result, name); - } - - /* Test invalid cases */ - for (i = 0; i < num_invalid_tests; i++) { - char name[100]; - - sprintf(name, "invalid test case: %s", invalid_tests_inputs[i]); - - ret = utils_parse_time_suffix(invalid_tests_inputs[i], &result); - ok(ret != 0, name); - } -} - -int main(int argc, char **argv) -{ - plan_tests(num_valid_tests + num_invalid_tests); - - diag("utils_parse_time_suffix tests"); - - test_utils_parse_time_suffix(); - - return exit_status(); -} diff --git a/tests/unit/test_utils_parse_time_suffix.cpp b/tests/unit/test_utils_parse_time_suffix.cpp new file mode 100644 index 000000000..95aac5561 --- /dev/null +++ b/tests/unit/test_utils_parse_time_suffix.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include + +#include + +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose = 3; +int lttng_opt_mi; + +struct valid_test_input { + const char *input; + uint64_t expected_result; +}; + +/* Valid test cases */ +static struct valid_test_input valid_tests_inputs[] = { + { "0", 0 }, + { "1234", 1234 }, + { "1234us", 1234 }, + { "16ms", 16000 }, + { "128ms", 128000 }, + { "32s", 32000000 }, + { "1m", 60000000 }, + { "20m", 1200000000 }, + { "1h", 3600000000 }, + { "5h", 18000000000 }, + { "00", 0 }, + { "0us", 0 }, + { "0ms", 0 }, + { "0s", 0 }, + { "0m", 0 }, + { "0h", 0 }, + { "00us", 0 }, + { "00ms", 0 }, + { "00s", 0 }, + { "00m", 0 }, + { "00h", 0 }, + { "12ms", 12000 }, + { "3597us", 3597 }, + { "+5", 5 }, + { "08", 8 }, + { "0145us", 145 }, +}; +static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); + +/* Invalid test cases */ +static const char *invalid_tests_inputs[] = { + "", + " ", + "-1", + "m", + "4611686018427387904s", + "0x40M", + "0x", + "x0", + "0xx0", + "07mm", + "0xm", + "0Xs", + "0x0ss", + "0a", + "0B", + "0x3 s", + "0xbs ", + "14ns", + "0xbs", + "14ns", + "14ms garbage after value", + "0x14s", + "0u", + "5mS", + "5Ms", + "12ussr", + "67msrp", + "14si", + "12mo", + "53hi", +}; +static const int num_invalid_tests = sizeof(invalid_tests_inputs) / sizeof(invalid_tests_inputs[0]); + +static void test_utils_parse_time_suffix(void) +{ + uint64_t result; + int ret; + int i; + + /* Test valid cases */ + for (i = 0; i < num_valid_tests; i++) { + char name[256]; + + ret = utils_parse_time_suffix(valid_tests_inputs[i].input, &result); + sprintf(name, "valid test case: %s expected %" PRIu64, valid_tests_inputs[i].input, result); + ok(ret == 0 && result == valid_tests_inputs[i].expected_result, name); + } + + /* Test invalid cases */ + for (i = 0; i < num_invalid_tests; i++) { + char name[100]; + + sprintf(name, "invalid test case: %s", invalid_tests_inputs[i]); + + ret = utils_parse_time_suffix(invalid_tests_inputs[i], &result); + ok(ret != 0, name); + } +} + +int main(int argc, char **argv) +{ + plan_tests(num_valid_tests + num_invalid_tests); + + diag("utils_parse_time_suffix tests"); + + test_utils_parse_time_suffix(); + + return exit_status(); +} diff --git a/tests/unit/test_uuid.c b/tests/unit/test_uuid.c deleted file mode 100644 index e13a09751..000000000 --- a/tests/unit/test_uuid.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2019 Michael Jeanson - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include - -#include - -#include "common/uuid.h" - -#define NR_TESTS 21 - -static const char valid_str_1[] = "3d260c88-75ea-47b8-a7e2-d6077c0378d9"; -static const char valid_str_2[] = "611cf3a6-a68b-4515-834f-208bc2762592"; -static const char valid_str_3[] = "1b4855cc-96de-4ae8-abe3-86449c2a43c4"; -static const char valid_str_4[] = "8ADED5B9-ACD2-439F-A60C-897403AA2AB4"; -static const char valid_str_5[] = "f109e0a2-C619-4d18-b760-20EA20E0F69A"; - -static lttng_uuid valid_uuid_1 = { - 0x3d, 0x26, 0x0c, 0x88, 0x75, 0xea, 0x47, 0xb8, - 0xa7, 0xe2, 0xd6, 0x07, 0x7c, 0x03, 0x78, 0xd9 -}; -static lttng_uuid valid_uuid_2 = { - 0x61, 0x1c, 0xf3, 0xa6, 0xa6, 0x8b, 0x45, 0x15, - 0x83, 0x4f, 0x20, 0x8b, 0xc2, 0x76, 0x25, 0x92 -}; -static lttng_uuid valid_uuid_3 = { - 0x1b, 0x48, 0x55, 0xcc, 0x96, 0xde, 0x4a, 0xe8, - 0xab, 0xe3, 0x86, 0x44, 0x9c, 0x2a, 0x43, 0xc4 -}; - -static const char invalid_str_1[] = "1b485!cc-96de-4XX8-abe3-86449c2a43?4"; -static const char invalid_str_2[] = "c2e6eddb&3955&4006&be3a&70bb63bd5f25"; -static const char invalid_str_3[] = "81b1cb88-ff42-45b9-ba4d-964088ee45"; -static const char invalid_str_4[] = "2d-6c6d756574-470e-9142-a4e6ad03f143"; -static const char invalid_str_5[] = "4542ad19-9e4f-4931-8261-2101c3e089ae7"; -static const char invalid_str_6[] = "XX0123"; - -static -void run_test_lttng_uuid_from_str(void) -{ - int ret; - lttng_uuid uuid1; - - /* - * Parse valid UUID strings, expect success. - */ - ret = lttng_uuid_from_str(valid_str_1, uuid1); - ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_1); - - ret = lttng_uuid_from_str(valid_str_2, uuid1); - ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_2); - - ret = lttng_uuid_from_str(valid_str_3, uuid1); - ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_3); - - ret = lttng_uuid_from_str(valid_str_4, uuid1); - ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_4); - - ret = lttng_uuid_from_str(valid_str_5, uuid1); - ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_5); - - /* - * Parse invalid UUID strings, expect failure. - */ - ret = lttng_uuid_from_str(invalid_str_1, uuid1); - ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_1); - - ret = lttng_uuid_from_str(invalid_str_2, uuid1); - ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_2); - - ret = lttng_uuid_from_str(invalid_str_3, uuid1); - ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_3); - - ret = lttng_uuid_from_str(invalid_str_4, uuid1); - ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_4); - - ret = lttng_uuid_from_str(invalid_str_5, uuid1); - ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_5); - - ret = lttng_uuid_from_str(invalid_str_6, uuid1); - ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_6); -} - -static -void run_test_lttng_uuid_to_str(void) -{ - char uuid_str[LTTNG_UUID_STR_LEN]; - - lttng_uuid_to_str(valid_uuid_1, uuid_str); - ok(strcmp(uuid_str, valid_str_1) == 0, "lttng_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_1); - - lttng_uuid_to_str(valid_uuid_2, uuid_str); - ok(strcmp(uuid_str, valid_str_2) == 0, "lttng_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_2); - - lttng_uuid_to_str(valid_uuid_3, uuid_str); - ok(strcmp(uuid_str, valid_str_3) == 0, "lttng_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_3); -} - -static -void run_test_lttng_uuid_is_equal(void) -{ - int ret; - lttng_uuid uuid1, uuid2; - - lttng_uuid_from_str(valid_str_1, uuid1); - lttng_uuid_from_str(valid_str_1, uuid2); - ret = lttng_uuid_is_equal(uuid1, uuid2); - ok(ret == true, "lttng_uuid_is_equal - Compare same UUID, expect success"); - - lttng_uuid_from_str(valid_str_2, uuid2); - ret = lttng_uuid_is_equal(uuid1, uuid2); - ok(ret == false, "lttng_uuid_is_equal - Compare different UUID, expect failure"); -} - -static -void run_test_lttng_uuid_copy(void) -{ - int ret; - lttng_uuid uuid1; - - lttng_uuid_copy(uuid1, valid_uuid_1); - ret = lttng_uuid_is_equal(uuid1, valid_uuid_1); - - ok(ret == true, "lttng_uuid_copy - Compare copied UUID with source, expect success"); -} - -static -void run_test_lttng_uuid_generate(void) -{ - int ret; - lttng_uuid uuid1, uuid2; - - lttng_uuid_generate(uuid1); - lttng_uuid_generate(uuid2); - - ok(lttng_uuid_is_equal(uuid1, uuid2) == false, "lttng_uuid_generate - Generated UUIDs are different"); - - /* - * Set the two most significant bits (bits 6 and 7) of the - * clock_seq_hi_and_reserved to zero and one, respectively. - */ - ret = uuid1[8] & (1 << 6); - ok(ret == 0, "lttng_uuid_generate - bit 6 of clock_seq_hi_and_reserved is set to zero"); - - ret = uuid1[8] & (1 << 7); - ok(ret != 0, "lttng_uuid_generate - bit 7 of clock_seq_hi_and_reserved is set to one"); - - /* - * Set the four most significant bits (bits 12 through 15) of the - * time_hi_and_version field to the 4-bit version number from - * Section 4.1.3. - */ - ret = uuid1[6] >> 4; - ok(ret == LTTNG_UUID_VER, "lttng_uuid_generate - Generated UUID version check"); -} - -static -void run_test(void) -{ - plan_tests(NR_TESTS); - - run_test_lttng_uuid_from_str(); - run_test_lttng_uuid_to_str(); - run_test_lttng_uuid_is_equal(); - run_test_lttng_uuid_copy(); - run_test_lttng_uuid_generate(); -} - -int main(int argc, char **argv) -{ - /* Run tap-formated tests */ - run_test(); - - return exit_status(); -} diff --git a/tests/unit/test_uuid.cpp b/tests/unit/test_uuid.cpp new file mode 100644 index 000000000..e13a09751 --- /dev/null +++ b/tests/unit/test_uuid.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2019 Michael Jeanson + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include + +#include + +#include "common/uuid.h" + +#define NR_TESTS 21 + +static const char valid_str_1[] = "3d260c88-75ea-47b8-a7e2-d6077c0378d9"; +static const char valid_str_2[] = "611cf3a6-a68b-4515-834f-208bc2762592"; +static const char valid_str_3[] = "1b4855cc-96de-4ae8-abe3-86449c2a43c4"; +static const char valid_str_4[] = "8ADED5B9-ACD2-439F-A60C-897403AA2AB4"; +static const char valid_str_5[] = "f109e0a2-C619-4d18-b760-20EA20E0F69A"; + +static lttng_uuid valid_uuid_1 = { + 0x3d, 0x26, 0x0c, 0x88, 0x75, 0xea, 0x47, 0xb8, + 0xa7, 0xe2, 0xd6, 0x07, 0x7c, 0x03, 0x78, 0xd9 +}; +static lttng_uuid valid_uuid_2 = { + 0x61, 0x1c, 0xf3, 0xa6, 0xa6, 0x8b, 0x45, 0x15, + 0x83, 0x4f, 0x20, 0x8b, 0xc2, 0x76, 0x25, 0x92 +}; +static lttng_uuid valid_uuid_3 = { + 0x1b, 0x48, 0x55, 0xcc, 0x96, 0xde, 0x4a, 0xe8, + 0xab, 0xe3, 0x86, 0x44, 0x9c, 0x2a, 0x43, 0xc4 +}; + +static const char invalid_str_1[] = "1b485!cc-96de-4XX8-abe3-86449c2a43?4"; +static const char invalid_str_2[] = "c2e6eddb&3955&4006&be3a&70bb63bd5f25"; +static const char invalid_str_3[] = "81b1cb88-ff42-45b9-ba4d-964088ee45"; +static const char invalid_str_4[] = "2d-6c6d756574-470e-9142-a4e6ad03f143"; +static const char invalid_str_5[] = "4542ad19-9e4f-4931-8261-2101c3e089ae7"; +static const char invalid_str_6[] = "XX0123"; + +static +void run_test_lttng_uuid_from_str(void) +{ + int ret; + lttng_uuid uuid1; + + /* + * Parse valid UUID strings, expect success. + */ + ret = lttng_uuid_from_str(valid_str_1, uuid1); + ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_1); + + ret = lttng_uuid_from_str(valid_str_2, uuid1); + ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_2); + + ret = lttng_uuid_from_str(valid_str_3, uuid1); + ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_3); + + ret = lttng_uuid_from_str(valid_str_4, uuid1); + ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_4); + + ret = lttng_uuid_from_str(valid_str_5, uuid1); + ok(ret == 0, "lttng_uuid_from_str - Parse valid string '%s', expect success", valid_str_5); + + /* + * Parse invalid UUID strings, expect failure. + */ + ret = lttng_uuid_from_str(invalid_str_1, uuid1); + ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_1); + + ret = lttng_uuid_from_str(invalid_str_2, uuid1); + ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_2); + + ret = lttng_uuid_from_str(invalid_str_3, uuid1); + ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_3); + + ret = lttng_uuid_from_str(invalid_str_4, uuid1); + ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_4); + + ret = lttng_uuid_from_str(invalid_str_5, uuid1); + ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_5); + + ret = lttng_uuid_from_str(invalid_str_6, uuid1); + ok(ret != 0, "lttng_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_6); +} + +static +void run_test_lttng_uuid_to_str(void) +{ + char uuid_str[LTTNG_UUID_STR_LEN]; + + lttng_uuid_to_str(valid_uuid_1, uuid_str); + ok(strcmp(uuid_str, valid_str_1) == 0, "lttng_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_1); + + lttng_uuid_to_str(valid_uuid_2, uuid_str); + ok(strcmp(uuid_str, valid_str_2) == 0, "lttng_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_2); + + lttng_uuid_to_str(valid_uuid_3, uuid_str); + ok(strcmp(uuid_str, valid_str_3) == 0, "lttng_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_3); +} + +static +void run_test_lttng_uuid_is_equal(void) +{ + int ret; + lttng_uuid uuid1, uuid2; + + lttng_uuid_from_str(valid_str_1, uuid1); + lttng_uuid_from_str(valid_str_1, uuid2); + ret = lttng_uuid_is_equal(uuid1, uuid2); + ok(ret == true, "lttng_uuid_is_equal - Compare same UUID, expect success"); + + lttng_uuid_from_str(valid_str_2, uuid2); + ret = lttng_uuid_is_equal(uuid1, uuid2); + ok(ret == false, "lttng_uuid_is_equal - Compare different UUID, expect failure"); +} + +static +void run_test_lttng_uuid_copy(void) +{ + int ret; + lttng_uuid uuid1; + + lttng_uuid_copy(uuid1, valid_uuid_1); + ret = lttng_uuid_is_equal(uuid1, valid_uuid_1); + + ok(ret == true, "lttng_uuid_copy - Compare copied UUID with source, expect success"); +} + +static +void run_test_lttng_uuid_generate(void) +{ + int ret; + lttng_uuid uuid1, uuid2; + + lttng_uuid_generate(uuid1); + lttng_uuid_generate(uuid2); + + ok(lttng_uuid_is_equal(uuid1, uuid2) == false, "lttng_uuid_generate - Generated UUIDs are different"); + + /* + * Set the two most significant bits (bits 6 and 7) of the + * clock_seq_hi_and_reserved to zero and one, respectively. + */ + ret = uuid1[8] & (1 << 6); + ok(ret == 0, "lttng_uuid_generate - bit 6 of clock_seq_hi_and_reserved is set to zero"); + + ret = uuid1[8] & (1 << 7); + ok(ret != 0, "lttng_uuid_generate - bit 7 of clock_seq_hi_and_reserved is set to one"); + + /* + * Set the four most significant bits (bits 12 through 15) of the + * time_hi_and_version field to the 4-bit version number from + * Section 4.1.3. + */ + ret = uuid1[6] >> 4; + ok(ret == LTTNG_UUID_VER, "lttng_uuid_generate - Generated UUID version check"); +} + +static +void run_test(void) +{ + plan_tests(NR_TESTS); + + run_test_lttng_uuid_from_str(); + run_test_lttng_uuid_to_str(); + run_test_lttng_uuid_is_equal(); + run_test_lttng_uuid_copy(); + run_test_lttng_uuid_generate(); +} + +int main(int argc, char **argv) +{ + /* Run tap-formated tests */ + run_test(); + + return exit_status(); +}