From 71fd47e1501df7ad7b6f19b3eeeb614149c9b336 Mon Sep 17 00:00:00 2001 From: Jevin Sweval Date: Fri, 29 Mar 2024 01:37:25 -0400 Subject: [PATCH] wip --- .../redirect-to-os-log/redirect-to-os-log.hpp | 33 ++--- lib/redirect-to-os-log-inject/CMakeLists.txt | 7 +- .../redirect-to-os-log-inject-lib.cpp | 34 ++++- lib/redirect-to-os-log/CMakeLists.txt | 4 +- .../redirect-to-os-log-lib.cpp | 122 +++++++++++++++++- .../redirect-to-os-log-inject/CMakeLists.txt | 2 +- test/lib/redirect-to-os-log/CMakeLists.txt | 4 +- .../redirect-to-os-log-lib-test.cpp | 6 +- test/test-helper/lib/CMakeLists.txt | 2 +- test/tool/CMakeLists.txt | 4 +- tool/CMakeLists.txt | 2 +- 11 files changed, 181 insertions(+), 39 deletions(-) diff --git a/include/redirect-to-os-log/redirect-to-os-log.hpp b/include/redirect-to-os-log/redirect-to-os-log.hpp index 3c6945b..635dfec 100644 --- a/include/redirect-to-os-log/redirect-to-os-log.hpp +++ b/include/redirect-to-os-log/redirect-to-os-log.hpp @@ -1,24 +1,19 @@ #pragma once -#include +#include -/** - * @brief Reports the name of the library - * - * Please see the note above for considerations when creating shared libraries. - */ -class [[gnu::visibility("default")]] exported_class { -public: - /** - * @brief Initializes the name field to the name of the project - */ - exported_class(); +namespace redirect_to_os_log { - /** - * @brief Returns a non-owning pointer to the string stored in this class - */ - auto name() const -> char const *; +[[gnu::visibility("default")]] +extern int exit_pipe[2]; -private: - std::string m_name; -}; +[[gnu::visibility("default")]] +extern ssize_t safe_write(int fd, const void *__nonnull buf, ssize_t count); + +[[gnu::visibility("default")]] +void *__nullable io_loop(void *__nonnull arg); + +[[gnu::visibility("default")]] +void setup_io_redirection(const bool is_injected); + +} // namespace redirect_to_os_log diff --git a/lib/redirect-to-os-log-inject/CMakeLists.txt b/lib/redirect-to-os-log-inject/CMakeLists.txt index ddcfd81..991a92d 100644 --- a/lib/redirect-to-os-log-inject/CMakeLists.txt +++ b/lib/redirect-to-os-log-inject/CMakeLists.txt @@ -1,5 +1,8 @@ add_library(redirect-to-os-log-inject SHARED redirect-to-os-log-inject-lib.cpp) target_compile_features(redirect-to-os-log-inject PRIVATE cxx_std_20) -set_target_properties(redirect-to-os-log-inject PROPERTIES CXX_EXTENSIONS ON) -target_compile_options(redirect-to-os-log-inject PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +set_target_properties(redirect-to-os-log-inject PROPERTIES + CXX_STANDARD 23 + CXX_EXTENSIONS ON +) +target_compile_options(redirect-to-os-log-inject PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_link_libraries(redirect-to-os-log-inject PRIVATE redirect-to-os-log-lib) diff --git a/lib/redirect-to-os-log-inject/redirect-to-os-log-inject-lib.cpp b/lib/redirect-to-os-log-inject/redirect-to-os-log-inject-lib.cpp index 9a2debb..10e12c0 100644 --- a/lib/redirect-to-os-log-inject/redirect-to-os-log-inject-lib.cpp +++ b/lib/redirect-to-os-log-inject/redirect-to-os-log-inject-lib.cpp @@ -1,6 +1,36 @@ +#include + #include +#include +#include + +#undef NDEBUG +#include + +namespace redirect_to_os_log_inject { + +static pthread_t io_loop_thread; [[gnu::constructor]] -void redirect_to_os_log_injector_ctor() { - fprintf(stderr, "redirect_to_os_log_injector_ctor called\n"); +static void redirect_to_os_log_injector_ctor() { + redirect_to_os_log::setup_io_redirection(true); + + // Spawn the I/O loop thread + pthread_create(&io_loop_thread, nullptr, redirect_to_os_log::io_loop, nullptr); } + +[[gnu::destructor]] +static void redirect_to_os_log_injector_dtor() { + // Signal the I/O loop thread to exit by writing to the pipe + redirect_to_os_log::safe_write(redirect_to_os_log::exit_pipe[1], "x", + 1); // The actual value written is irrelevant + + // Wait for the thread to exit + pthread_join(io_loop_thread, nullptr); + + // Close the pipe + assert(!close(redirect_to_os_log::exit_pipe[0])); + assert(!close(redirect_to_os_log::exit_pipe[1])); +} + +} // namespace redirect_to_os_log_inject diff --git a/lib/redirect-to-os-log/CMakeLists.txt b/lib/redirect-to-os-log/CMakeLists.txt index 896272b..a9dcdc7 100644 --- a/lib/redirect-to-os-log/CMakeLists.txt +++ b/lib/redirect-to-os-log/CMakeLists.txt @@ -3,6 +3,8 @@ add_library(redirect-to-os-log-lib STATIC redirect-to-os-log-lib.cpp ${CMAKE_CUR set_target_properties( redirect-to-os-log-lib PROPERTIES PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/../../include/redirect-to-os-log/redirect-to-os-log.hpp + CXX_STANDARD 23 + CXX_EXTENSIONS ON CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES VERSION "${PROJECT_VERSION}" @@ -10,5 +12,5 @@ set_target_properties( EXPORT_NAME redirect-to-os-log OUTPUT_NAME redirect-to-os-log ) -target_compile_options(redirect-to-os-log-lib PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(redirect-to-os-log-lib PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_include_directories(redirect-to-os-log-lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../include) diff --git a/lib/redirect-to-os-log/redirect-to-os-log-lib.cpp b/lib/redirect-to-os-log/redirect-to-os-log-lib.cpp index 9b821d7..2e3caec 100644 --- a/lib/redirect-to-os-log/redirect-to-os-log-lib.cpp +++ b/lib/redirect-to-os-log/redirect-to-os-log-lib.cpp @@ -1,7 +1,123 @@ #include "redirect-to-os-log/redirect-to-os-log.hpp" -exported_class::exported_class() : m_name{"redirect-to-os-log"} {} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -auto exported_class::name() const -> char const * { - return m_name.c_str(); +#undef NDEBUG +#include + +extern "C" const char *const *const environ; + +namespace redirect_to_os_log { + +static int stdout_pipe[2]; +static int stderr_pipe[2]; +int exit_pipe[2]; +static int original_stdout; +static int original_stderr; + +ssize_t safe_write(int fd, const void *__nonnull buf, ssize_t count) { + const auto written = count; + auto buffer = static_cast(buf); + assert(count >= 0); + + while (count > 0) { + ssize_t res = write(fd, buffer, static_cast(count)); + if (res < 0) { + if (errno == EINTR) { + continue; // Interrupted by a signal, try again + } else { + assert(false && "safe_write failed"); + } + } + buffer += res; + count -= res; + } + return written; } + +void log_output(int fd, os_log_t logger, bool echo) { + char buffer[1024]; + ssize_t bytes_read; + + while ((bytes_read = read(fd, buffer, sizeof(buffer) - 1)) > 0) { + buffer[bytes_read] = '\0'; + if (echo) { + safe_write(fd == STDOUT_FILENO ? STDOUT_FILENO : STDERR_FILENO, buffer, bytes_read); + } + os_log_with_type(logger, fd == STDOUT_FILENO ? OS_LOG_TYPE_DEFAULT : OS_LOG_TYPE_ERROR, + "%{public}s", buffer); + } +} + +void setup_io_redirection(const bool is_injected) { + // Create pipes for stdout and stderr + assert(!pipe(stdout_pipe)); + assert(!pipe(stderr_pipe)); + if (is_injected) { + assert(!pipe(exit_pipe)); + } + + // Duplicate the original stdout and stderr + original_stdout = dup(STDOUT_FILENO); + assert(original_stdout >= 0); + original_stderr = dup(STDERR_FILENO); + assert(original_stderr >= 0); + + // Redirect stdout and stderr to the write ends of the pipes + assert(dup2(stdout_pipe[1], STDOUT_FILENO) == STDOUT_FILENO); + assert(dup2(stderr_pipe[1], STDERR_FILENO) == STDERR_FILENO); + + // Close the original write ends of the pipes + assert(!close(stdout_pipe[1])); + assert(!close(stderr_pipe[1])); +} + +void *__nullable io_loop(void *__nonnull arg) { + const auto is_injected = *reinterpret_cast(arg); + struct kevent kev[3]; + int kq = kqueue(); + assert(kq >= 0); + + // Set up the kevents to monitor the read ends of the pipes + EV_SET(&kev[0], stdout_pipe[0], EVFILT_READ, EV_ADD, 0, 0, NULL); + EV_SET(&kev[1], stderr_pipe[0], EVFILT_READ, EV_ADD, 0, 0, NULL); + if (is_injected) { + EV_SET(&kev[2], exit_pipe[0], EVFILT_READ, EV_ADD, 0, 0, NULL); + } + kevent(kq, kev, 2 + is_injected, NULL, 0, NULL); + + while (true) { + // Wait for events + struct kevent event_list[2]; + int nev = kevent(kq, NULL, 0, event_list, 2, NULL); + for (int i = 0; i < nev; i++) { + int fd = event_list[i].ident; + if (fd == stdout_pipe[0] || fd == stderr_pipe[0]) { + // Data is available to read, echo it to the original stdout or stderr + char buffer[1024]; + ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); + if (bytes_read > 0) { + int output_fd = (fd == stdout_pipe[0]) ? original_stdout : original_stderr; + safe_write(output_fd, buffer, bytes_read); + } + } + } + } + + close(kq); + pthread_exit(nullptr); + return nullptr; +} + +} // namespace redirect_to_os_log diff --git a/test/lib/redirect-to-os-log-inject/CMakeLists.txt b/test/lib/redirect-to-os-log-inject/CMakeLists.txt index fb0492d..40cbf98 100644 --- a/test/lib/redirect-to-os-log-inject/CMakeLists.txt +++ b/test/lib/redirect-to-os-log-inject/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(redirect-to-os-log-inject-lib-test redirect-to-os-log-inject-lib-test.cpp) target_compile_features(redirect-to-os-log-inject-lib-test PRIVATE cxx_std_20) set_target_properties(redirect-to-os-log-inject-lib-test PROPERTIES CXX_EXTENSIONS ON) -target_compile_options(redirect-to-os-log-inject-lib-test PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(redirect-to-os-log-inject-lib-test PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_link_libraries(redirect-to-os-log-inject-lib-test PRIVATE test-helper) add_test(NAME redirect-to-os-log-inject-lib-test COMMAND redirect-to-os-log-inject-lib-test) diff --git a/test/lib/redirect-to-os-log/CMakeLists.txt b/test/lib/redirect-to-os-log/CMakeLists.txt index 945481f..f4e67d3 100644 --- a/test/lib/redirect-to-os-log/CMakeLists.txt +++ b/test/lib/redirect-to-os-log/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(redirect-to-os-log-lib-test redirect-to-os-log-lib-test.cpp) -target_compile_features(redirect-to-os-log-lib-test PRIVATE cxx_std_20) +target_compile_features(redirect-to-os-log-lib-test PRIVATE cxx_std_23) set_target_properties(redirect-to-os-log-lib-test PROPERTIES CXX_EXTENSIONS ON) -target_compile_options(redirect-to-os-log-lib-test PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(redirect-to-os-log-lib-test PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_link_libraries(redirect-to-os-log-lib-test PRIVATE redirect-to-os-log-lib) add_test(NAME redirect-to-os-log-lib-test COMMAND redirect-to-os-log-lib-test) diff --git a/test/lib/redirect-to-os-log/redirect-to-os-log-lib-test.cpp b/test/lib/redirect-to-os-log/redirect-to-os-log-lib-test.cpp index c46a59a..25f47b6 100644 --- a/test/lib/redirect-to-os-log/redirect-to-os-log-lib-test.cpp +++ b/test/lib/redirect-to-os-log/redirect-to-os-log-lib-test.cpp @@ -2,8 +2,4 @@ #include "redirect-to-os-log/redirect-to-os-log.hpp" -auto main() -> int { - auto const exported = exported_class{}; - - return std::string("redirect-to-os-log") == exported.name() ? 0 : 1; -} +auto main() -> int {} diff --git a/test/test-helper/lib/CMakeLists.txt b/test/test-helper/lib/CMakeLists.txt index b9e38ae..621398f 100644 --- a/test/test-helper/lib/CMakeLists.txt +++ b/test/test-helper/lib/CMakeLists.txt @@ -8,5 +8,5 @@ set_target_properties( CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES ) -target_compile_options(test-helper PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(test-helper PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_include_directories(test-helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) diff --git a/test/tool/CMakeLists.txt b/test/tool/CMakeLists.txt index 9ff44b6..856f8f3 100644 --- a/test/tool/CMakeLists.txt +++ b/test/tool/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(redirect-to-os-log-tool-test-subprocess redirect-to-os-log-tool-test-subprocess.cpp) target_compile_features(redirect-to-os-log-tool-test-subprocess PRIVATE cxx_std_20) set_target_properties(redirect-to-os-log-tool-test-subprocess PROPERTIES CXX_EXTENSIONS ON) -target_compile_options(redirect-to-os-log-tool-test-subprocess PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(redirect-to-os-log-tool-test-subprocess PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_link_libraries(redirect-to-os-log-tool-test-subprocess PRIVATE test-helper) add_dependencies(redirect-to-os-log-tool-test-subprocess redirect-to-os-log) @@ -11,7 +11,7 @@ add_test(NAME redirect-to-os-log-tool-test-subprocess COMMAND redirect-to-os-log add_executable(redirect-to-os-log-tool-test redirect-to-os-log-tool-test.cpp) target_compile_features(redirect-to-os-log-tool-test PRIVATE cxx_std_20) set_target_properties(redirect-to-os-log-tool-test PROPERTIES CXX_EXTENSIONS ON) -target_compile_options(redirect-to-os-log-tool-test PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(redirect-to-os-log-tool-test PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) add_dependencies(redirect-to-os-log-tool-test redirect-to-os-log redirect-to-os-log-tool-test-subprocess) add_test(NAME redirect-to-os-log-tool-test COMMAND redirect-to-os-log-tool-test) diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt index 9ab02f1..ca30ea3 100644 --- a/tool/CMakeLists.txt +++ b/tool/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(redirect-to-os-log redirect-to-os-log.cpp) target_compile_features(redirect-to-os-log PRIVATE cxx_std_20) set_target_properties(redirect-to-os-log PROPERTIES CXX_EXTENSIONS ON) -target_compile_options(redirect-to-os-log PRIVATE -Wall -Wextra -Wpedantic -Wno-gnu-line-marker) +target_compile_options(redirect-to-os-log PRIVATE -Wall -Wextra -Wpedantic -Wno-nullability-extension -Wno-gnu-line-marker) target_link_libraries(redirect-to-os-log PRIVATE redirect-to-os-log-lib)