From 12ebc8222c5d12b3f4c47cfc4d90c879b5dffc3b Mon Sep 17 00:00:00 2001 From: Jevin Sweval Date: Sat, 30 Mar 2024 11:13:14 -0400 Subject: [PATCH] broken wip --- .gitmodules | 3 + 3rdparty/argparse | 1 + CMakeLists.txt | 1 + .../redirect-to-os-log/redirect-to-os-log.hpp | 3 - .../redirect-to-os-log-inject-lib.cpp | 5 +- .../redirect-to-os-log-lib.cpp | 2 +- tool/CMakeLists.txt | 2 +- tool/redirect-to-os-log.cpp | 148 +++++++++++++++++- 8 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 .gitmodules create mode 160000 3rdparty/argparse diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..302e690 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/argparse"] + path = 3rdparty/argparse + url = https://github.com/p-ranav/argparse diff --git a/3rdparty/argparse b/3rdparty/argparse new file mode 160000 index 0000000..a1c41c5 --- /dev/null +++ b/3rdparty/argparse @@ -0,0 +1 @@ +Subproject commit a1c41c5537c919c1a56661ec1cdf5a49b9e99af6 diff --git a/CMakeLists.txt b/CMakeLists.txt index a6165a9..d9858b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ project( include(cmake/variables.cmake) +add_subdirectory(3rdparty/argparse EXCLUDE_FROM_ALL) add_subdirectory(lib) add_subdirectory(tool) if(BUILD_TESTING) 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 01a97e7..8c81d12 100644 --- a/include/redirect-to-os-log/redirect-to-os-log.hpp +++ b/include/redirect-to-os-log/redirect-to-os-log.hpp @@ -13,9 +13,6 @@ struct log_args { [[gnu::visibility("default")]] extern int exit_pipe[2]; -[[gnu::visibility("default")]] -extern bool should_echo_from_env_var(); - [[gnu::visibility("default")]] extern ssize_t safe_write(int fd, const void *__nonnull buf, ssize_t count); 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 9099735..36cdebe 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 @@ -15,9 +15,8 @@ static pthread_t io_loop_thread; [[gnu::constructor]] static void redirect_to_os_log_injector_ctor() { // Spawn the I/O loop thread - redirect_to_os_log::log_args args = {.is_injected = true, - .echo = redirect_to_os_log::should_echo_from_env_var(), - .subsystem = getprogname()}; + redirect_to_os_log::log_args args = { + .is_injected = true, .echo = false, .subsystem = getprogname()}; assert(!pthread_create(&io_loop_thread, nullptr, redirect_to_os_log::io_loop, reinterpret_cast(&args))); } 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 811936c..b6cb77a 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 @@ -116,7 +116,7 @@ static void setup_io_redirection(const char *const __nonnull subsystem, const bo void *__nullable io_loop(void *__nonnull arg) { const auto args = reinterpret_cast(arg); const auto is_injected = args->is_injected; - const auto echo = args->echo; + const auto echo = args->echo || should_echo_from_env_var(); setup_io_redirection(args->subsystem, is_injected); struct kevent kev[3]; diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt index ca30ea3..b199a2e 100644 --- a/tool/CMakeLists.txt +++ b/tool/CMakeLists.txt @@ -2,4 +2,4 @@ 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-nullability-extension -Wno-gnu-line-marker) -target_link_libraries(redirect-to-os-log PRIVATE redirect-to-os-log-lib) +target_link_libraries(redirect-to-os-log PRIVATE argparse redirect-to-os-log-lib) diff --git a/tool/redirect-to-os-log.cpp b/tool/redirect-to-os-log.cpp index ad9de68..f8e0229 100644 --- a/tool/redirect-to-os-log.cpp +++ b/tool/redirect-to-os-log.cpp @@ -1,10 +1,152 @@ +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +static void print_usage() { + fprintf(stderr, "Usage: %s [-e(cho)] \n", + getprogname()); +} + +// retval: 0 - in group, 1 - not in group, 2 - getgroups error +static int am_member_of_group(gid_t gid) { + gid_t groups[NGROUPS_MAX + 2]; + groups[0] = getegid(); + const int ngroups = getgroups(NGROUPS_MAX + 1, groups + 1); + if (ngroups < 0) { + const auto cerrno = errno; + fprintf(stderr, "Error calling getgroups()! res: %d errno: %d aka '%s'\n", ngroups, cerrno, + strerror(cerrno)); + return 2; + } + for (int i = 0; i < ngroups + 1; ++i) { + if (groups[i] == gid) { + return 0; + } + } + return 1; +} + +// retval: 0 - is executable, 1 - not executable, 2 - doesn't exist, 3 - not regular file, 4 - stat +// error, 5 - other error +static int is_exe_executable(const fs::path &exe_path, bool quiet = true) { + std::error_code ec; + fs::file_status fst = fs::status(exe_path, ec); + if (ec) { + fprintf(stderr, "Error getting status of '%s' error code: %d aka '%s'\n", exe_path.c_str(), + ec.value(), strerror(ec.value())); + return 5; + } + const auto ftype = fst.type(); + if (ftype == fs::file_type::not_found) { + if (!quiet) { + fprintf(stderr, "Executable '%s' doesn't exist.\n", exe_path.c_str()); + } + return 2; + } + if (ftype != fs::file_type::regular) { + if (!quiet) { + fprintf(stderr, "Executable '%s' is not a regular file.\n", exe_path.c_str()); + } + return 3; + } + struct stat st; + int stat_res; + if ((stat_res = stat(exe_path.c_str(), &st)) < 0) { + if (!quiet) { + const auto cerrno = errno; + fprintf(stderr, "Failed to stat '%s' stat() res: %d errno: %d aka '%s'\n", + exe_path.c_str(), stat_res, cerrno, strerror(cerrno)); + } + return 4; + } + const auto fperms = fst.permissions(); + if ((fperms & fs::perms::others_exec) != fs::perms::none) { + return 0; + } + if (((fperms & fs::perms::owner_exec) != fs::perms::none) && geteuid() == st.st_uid) { + return 0; + } + if (((fperms & fs::perms::group_exec) != fs::perms::none) && !am_member_of_group(st.st_gid)) { + return 0; + } + return 1; +} + +static std::optional is_exe_in_path(const fs::path &exe_path) { + if (const auto path_var_cstr = getenv("PATH")) { + const auto path_var = std::string{path_var_cstr}; + auto path_sstream = std::stringstream{path_var}; + std::string path_part; + while (std::getline(path_sstream, path_part, ':')) { + const auto test_exe_path = fs::path{path_part} / exe_path; + if (!is_exe_executable(test_exe_path, true)) { + return test_exe_path; + } + } + } + return {}; +} + int main(int argc, const char *const *argv) { + bool do_echo = false; + if (argc < 2) { + print_usage(); + return 1; + } + if (!strcmp(argv[1], "-e")) { + do_echo = true; + if (argc < 3) { + print_usage(); + return 1; + } + } + fs::path exe_path{argv[1 + do_echo]}; + if (is_exe_executable(exe_path)) { + std::error_code canon_ec; +#if __has_feature(cxx_exceptions) + try { +#endif + exe_path = fs::canonical(exe_path, canon_ec); +#if __has_feature(cxx_exceptions) + } catch (const fs::filesystem_error &e) { + fprintf(stderr, "Couldn't canonicalize exe path '%s' reason: '%s'\n", exe_path.c_str(), + e.what()); + return 2; + } +#endif + if (canon_ec) { + fprintf(stderr, "Couldn't canonicalize exe path '%s' errno: %d aka '%s'\n", + exe_path.c_str(), canon_ec.value(), strerror(canon_ec.value())); + return 2; + } + } else { + if (auto exe_from_path_var = is_exe_in_path(exe_path)) { + exe_path = *exe_from_path_var; + } else { + fprintf(stderr, "Couldn't find exe '%s' in PATH\n", exe_path.c_str()); + return 2; + } + } + + const char *const *exe_argv = &argv[2 + do_echo]; + const auto subsystem = exe_path.filename().c_str(); - // Spawn the I/O loop thread - bool is_injected = false; - redirect_to_os_log::io_loop(reinterpret_cast(is_injected)); + // Run the I/O loop thread + redirect_to_os_log::log_args args = { + .is_injected = false, .echo = do_echo, .subsystem = subsystem}; + redirect_to_os_log::io_loop(reinterpret_cast(&args)); return 0; }