Skip to content

Commit

Permalink
broken wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jevinskie committed Mar 30, 2024
1 parent f233190 commit 12ebc82
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "3rdparty/argparse"]
path = 3rdparty/argparse
url = https://github.com/p-ranav/argparse
1 change: 1 addition & 0 deletions 3rdparty/argparse
Submodule argparse added at a1c41c
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 0 additions & 3 deletions include/redirect-to-os-log/redirect-to-os-log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<void *>(&args)));
}
Expand Down
2 changes: 1 addition & 1 deletion lib/redirect-to-os-log/redirect-to-os-log-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const log_args *>(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];
Expand Down
2 changes: 1 addition & 1 deletion tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
148 changes: 145 additions & 3 deletions tool/redirect-to-os-log.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,152 @@
#include <argparse/argparse.hpp>
#include <redirect-to-os-log/redirect-to-os-log.hpp>

#include <cerrno>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <sstream>
#include <sys/stat.h>
#include <system_error>
#include <unistd.h>

namespace fs = std::filesystem;

static void print_usage() {
fprintf(stderr, "Usage: %s [-e(cho)] <executable> <optional executable arguments>\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<fs::path> 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<void *>(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<void *>(&args));

return 0;
}

0 comments on commit 12ebc82

Please sign in to comment.