Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/catter/core/app_config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "app_config.h"

#include <format>
#include <fstream>
#include <iterator>
#include <string_view>
#include <unordered_map>

namespace catter::app {

StartupConfig to_startup_config(const core::Option::CatterOption& opt) {
return StartupConfig{
.log = true,
.mode = *opt.mode,
.script_path = *opt.script_path,
.script_args = opt.script_args.has_value() ? *opt.script_args : std::vector<std::string>{},
.build_system_command = *opt.args,
.working_dir = opt.working_dir.has_value() ? std::filesystem::absolute(*opt.working_dir)
: std::filesystem::current_path(),
};
}

RuntimePlan build_runtime_plan(const StartupConfig& config) {
struct ModeMeta {
ipc::ServiceMode mode;
js::CatterRuntime runtime;
};

const static std::unordered_map<std::string_view, ModeMeta> mode_map = {
{"inject",
{.mode = ipc::ServiceMode::INJECT,
.runtime = {
.supportActions = {js::ActionType::drop,
js::ActionType::skip,
js::ActionType::modify},
.supportEvents = {js::EventType::finish},
.type = js::CatterRuntime::Type::inject,
.supportParentId = true,
}}}
};

auto it = mode_map.find(config.mode);
if(it == mode_map.end()) {
throw std::runtime_error(std::format("Unsupported mode: {}", config.mode));
}

return RuntimePlan{
.log = config.log,
.mode = it->second.mode,
.script_path = config.script_path,
.script_args = config.script_args,
.build_system_command = config.build_system_command,
.working_dir = config.working_dir,
.runtime = &it->second.runtime,
};
}

std::string load_script_content(const std::string& script_path) {
const static std::unordered_map<std::string_view, std::string_view> internal_scripts = {
{"script::cdb",
R"(
import { scripts, service } from "catter";
service.register(new scripts.CDB());
)"}
};

if(auto it = internal_scripts.find(script_path); it != internal_scripts.end()) {
return std::string(it->second);
}

std::ifstream ifs{script_path};
if(!ifs.good()) {
throw std::runtime_error(std::format("Failed to open script file: {}", script_path));
}

return std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
}

} // namespace catter::app
38 changes: 38 additions & 0 deletions src/catter/core/app_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include <filesystem>
#include <string>
#include <vector>

#include "capi/type.h"
#include "ipc.h"
#include "opt/main/option.h"

namespace catter::app {

struct StartupConfig {
bool log;
std::string mode;
std::string script_path;
std::vector<std::string> script_args;
std::vector<std::string> build_system_command;
std::filesystem::path working_dir;
};

struct RuntimePlan {
bool log;
ipc::ServiceMode mode;
std::string script_path;
std::vector<std::string> script_args;
std::vector<std::string> build_system_command;
std::filesystem::path working_dir;
const js::CatterRuntime* runtime;
};

StartupConfig to_startup_config(const core::Option::CatterOption& opt);

RuntimePlan build_runtime_plan(const StartupConfig& config);

std::string load_script_content(const std::string& script_path);

} // namespace catter::app
148 changes: 148 additions & 0 deletions src/catter/core/app_runner.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include "app_runner.h"

#include <cstdint>
#include <format>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>

#include "app_config.h"
#include "capi/type.h"
#include "ipc.h"
#include "js.h"
#include "qjs.h"
#include "session.h"
#include "config/catter-proxy.h"
#include "util/crossplat.h"
#include "util/data.h"

namespace catter::app {
namespace {

class ServiceImpl : public ipc::InjectService {
public:
ServiceImpl(data::ipcid_t id, const js::CatterRuntime* runtime) : id(id), runtime(runtime) {}

~ServiceImpl() override = default;

data::ipcid_t create(data::ipcid_t parent_id) override {
this->parent_id = parent_id;
return this->id;
}

data::action make_decision(data::command cmd) override {
auto act = js::on_command(this->id,
js::CommandData{
.cwd = cmd.cwd,
.exe = cmd.executable,
.argv = cmd.args,
.env = cmd.env,
.runtime = *runtime,
.parent = this->parent_id,
});

switch(act.type()) {
case js::ActionType::drop: {
return data::action{.type = data::action::DROP, .cmd = {}};
}
case js::ActionType::skip: {
return data::action{.type = data::action::INJECT, .cmd = cmd};
}
case js::ActionType::modify: {
auto& tag = act.get<js::ActionType::modify>();
return data::action{
.type = data::action::INJECT,
.cmd = {
.cwd = std::move(tag.data.cwd),
.executable = std::move(tag.data.exe),
.args = std::move(tag.data.argv),
.env = std::move(tag.data.env),
}
};
}
default: throw std::runtime_error("Unhandled action type");
}
}

void finish(int64_t code) override {
js::on_execution(this->id, js::Tag<js::EventType::finish>{.code = code});
}

void report_error(data::ipcid_t parent_id, std::string error_msg) override {
js::on_command(id, std::unexpected(js::CatterErr{.msg = std::move(error_msg)}));
}

struct Factory {
const js::CatterRuntime* runtime;

std::unique_ptr<ServiceImpl> operator() (data::ipcid_t id) const {
return std::make_unique<ServiceImpl>(id, runtime);
}
};

private:
data::ipcid_t id = 0;
data::ipcid_t parent_id = 0;
const js::CatterRuntime* runtime = nullptr;
};

int64_t inject(const js::CatterConfig& config) {

auto proxy_path = util::get_catter_root_path() / config::proxy::EXE_NAME;
Session::ProcessLaunchPlan launch_plan{
.executable = proxy_path.string(),
.args =
{
proxy_path.string(),
"-p", "0",
"--exec", config.buildSystemCommand.front(),
"--", },
};
append_range_to_vector(launch_plan.args, config.buildSystemCommand);

Session session;
auto session_plan = Session::make_run_plan(std::move(launch_plan),
ServiceImpl::Factory{.runtime = &config.runtime});

return session.run(std::move(session_plan));
}

int64_t execute_service(ipc::ServiceMode mode, const js::CatterConfig& config) {
switch(mode) {
case ipc::ServiceMode::INJECT: {
return inject(config);
}
default: {
throw std::runtime_error(
std::format("UnExpected mode: {:0x}", static_cast<uint8_t>(mode)));
}
}
throw std::runtime_error("Not implemented");
}

} // namespace

void run(const core::Option::CatterOption& opt) {
auto startup = to_startup_config(opt);
auto plan = build_runtime_plan(startup);
auto script_content = load_script_content(plan.script_path);

js::init_qjs({.pwd = plan.working_dir});
js::run_js_file(script_content, plan.script_path);

auto config = js::on_start({
.scriptPath = plan.script_path,
.scriptArgs = plan.script_args,
.buildSystemCommand = plan.build_system_command,
.runtime = *plan.runtime,
.options = {.log = plan.log},
.isScriptSupported = true,
});

js::on_finish(js::Tag<js::EventType::finish>{
.code = execute_service(plan.mode, config),
});
}

} // namespace catter::app
9 changes: 9 additions & 0 deletions src/catter/core/app_runner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include "opt/main/option.h"

namespace catter::app {

void run(const core::Option::CatterOption& opt);

} // namespace catter::app
35 changes: 30 additions & 5 deletions src/catter/core/session.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#include <cassert>
#include <coroutine>
#include <filesystem>
#include <format>
#include <functional>
#include <list>
#include <ranges>
#include <stdexcept>
Expand All @@ -18,8 +15,36 @@

namespace catter {

eventide::task<void> Session::loop(
eventide::function_ref<eventide::task<void>(data::ipcid_t, eventide::pipe&&)> acceptor) {
int64_t Session::run(RunPlan run_plan) {
#ifndef _WIN32
if(std::filesystem::exists(config::ipc::pipe_name())) {
std::filesystem::remove(config::ipc::pipe_name());
}
#endif
auto acc_ret =
eventide::pipe::listen(config::ipc::pipe_name(), eventide::pipe::options(), default_loop());

if(!acc_ret) {
throw std::runtime_error(
std::format("Failed to create acceptor: {}", acc_ret.error().message()));
}

this->acc = std::make_unique<PipeAcceptor>(std::move(*acc_ret));

auto loop_task = this->loop(std::move(run_plan.callback));
auto spawn_task = this->spawn(std::move(run_plan.launch_plan.executable),
std::move(run_plan.launch_plan.args));

default_loop().schedule(loop_task);
default_loop().schedule(spawn_task);

default_loop().run();

loop_task.result(); // Propagate exceptions from loop task
return spawn_task.result();
}

eventide::task<void> Session::loop(ClientAcceptor acceptor) {
std::list<eventide::task<void>> linked_clients;
for(auto i: std::views::iota(data::ipcid_t(1))) {
auto client = co_await this->acc->accept();
Expand Down
Loading
Loading