Program Listing for File MainProcess.cpp

Return to documentation for file (include/input/IPC/transmission/linux/MainProcess.cpp)

#include "MainProcess.hpp"
#include "PDJE_INPUT_PROCESS_HASH.hpp"
#include "httplib.h"
#include "ipc_util.hpp"
#include <chrono>
#include <format>
#include <iostream>
#include <spawn.h>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <thread>
#include <unistd.h>
namespace PDJE_IPC {
static std::string
GenCommand(const std::string &store_value,
           const std::string &command,
           const std::string &arg1,
           const std::string &arg2)
{
    return store_value + "=$(" + command + " " + arg1 + " " + arg2 + ");";
}
static std::string
GenIF(const std::string &A,
      const std::string &plain_text,
      const std::string &THEN)
{
    return std::format("[[ \"${}\" == \"{}\" ]] &&", A, plain_text) + " { " +
           THEN + " };";
}
static std::string
GenExecuteShell(const fs::path &exec_path, const int port)
{
    std::string tmpBlob =
        "umask 077; tmp=$(mktemp /dev/shm/blob.XXXXXX) || exit 1;";
    tmpBlob +=
        "trap '[ -n \"${tmp:-}\" ] && { shred -u -- \"$tmp\" 2>/dev/null "
        "|| rm -f -- \"$tmp\"; }' EXIT;";
    std::string fillTMP = std::format("cat -- {} > \"$tmp\";",
                                      "\"" + exec_path.generic_string() + "\"");
    auto        command = GenCommand("exec_hash",
                              "sha256sum --",
                              ("\"$tmp\""),
                              "| tr -s ' ' | cut -d ' ' -f1");
    std::string then    = "chmod 700 \"$tmp\";exec {fd}<\"$tmp\";rm "
                          "-f -- \"$tmp\";trap - EXIT; exec \"/proc/$$/fd/$fd\" " +
                       std::to_string(port) + " || exit 2; ";
    command += GenIF("exec_hash", EMBEDDED_INPUT_PROCESS_SHA256, then);
    return tmpBlob + fillTMP + command;
}

static bool
OpenProcess(const fs::path &pt, pid_t *child_pid, const int port)
{
    auto        bash  = "/bin/bash";
    std::string Shell = GenExecuteShell(pt, port);
    std::cout << Shell << std::endl;
    char *pkexec_args[] = { (char *)"pkexec",
                            (char *)bash,
                            (char *)"-c",
                            (char *)Shell.c_str(),
                            nullptr };
    char *sudo_args[]   = { (char *)"sudo",
                            (char *)bash,
                            (char *)"-c",
                            (char *)Shell.c_str(),
                            nullptr };

    if ((getenv("DISPLAY") || getenv("WAYLAND_DISPLAY")) &&
        access("/usr/bin/pkexec", X_OK) == 0) {
        int spawn_stat = posix_spawn(child_pid,
                                     "/usr/bin/pkexec",
                                     nullptr,
                                     nullptr,
                                     pkexec_args,
                                     environ);
        if (spawn_stat == 0) {
            return true;
        }
    } else {
        int spawn_stat = posix_spawn(
            child_pid, "/usr/bin/sudo", nullptr, nullptr, sudo_args, environ);
        if (spawn_stat == 0) {
            return true;
        }
    }
    return false;
}

MainProcess::MainProcess(const int port)
{
    unlink(imp.socket_path.c_str());
    imp.socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);

    sockaddr_un address_temp{};
    address_temp.sun_family = AF_UNIX;

    if (bind(imp.socket_fd,
             reinterpret_cast<sockaddr *>(&address_temp),
             sizeof(address_temp)) < 0) {
        critlog("failed to bind socket fd. errno:");
        critlog(errno);

        return;
    }

    if (listen(imp.socket_fd, 1) < 0) {
        critlog("failed to listen socket. errno:");
        critlog(errno);
        return;
    }
    auto path = GetValidProcessExecutor();
    if (!OpenProcess(path, &imp.child_pid, port)) {
        critlog("failed to open child process. errno:");
        critlog(errno);
        return;
    }

    // imp.child_fd = accept(imp.socket_fd, nullptr, nullptr);
    // if (imp.child_fd < 0) {
    //     critlog("failed to get child process fd. errno:");
    //     critlog(errno);
    //     return;
    // }
    cli.emplace("127.0.0.1", port);
    cli->set_connection_timeout(0, 200'000); // 200ms
    cli->set_read_timeout(0, 200'000);
    cli->set_write_timeout(0, 200'000);
    while (true) {
        if (auto res = cli->Get("/health"); res && res->status == 200) {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

MainProcess::~MainProcess()
{
    if (imp.child_fd >= 0) {
        close(imp.child_fd);
    }
    if (imp.socket_fd >= 0) {
        close(imp.socket_fd);
    }
    unlink(imp.socket_path.c_str());
}
bool
MainProcess::EndTransmission()
{
    auto res = cli->Get("/stop");
    if (res) {
        return true;
    } else {
        return false;
    }
}
}; // namespace PDJE_IPC