d3af7d54ff
- CMakeLists.txt - main.cpp - data_collectors.cpp - data_collectors.h - runner.cpp - runner.h Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
125 lines
3.2 KiB
C++
125 lines
3.2 KiB
C++
#include "runner.h"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace odr {
|
|
|
|
namespace {
|
|
|
|
long long now_ms() {
|
|
using namespace std::chrono;
|
|
return duration_cast<milliseconds>(
|
|
system_clock::now().time_since_epoch()).count();
|
|
}
|
|
|
|
std::string make_uid() {
|
|
static std::atomic<int> ctr{0};
|
|
long long ts = now_ms();
|
|
int n = ctr.fetch_add(1, std::memory_order_relaxed);
|
|
char buf[64];
|
|
std::snprintf(buf, sizeof(buf), "%lld_%05d", ts, n);
|
|
return buf;
|
|
}
|
|
|
|
bool write_file(const fs::path& p, const std::string& bytes) {
|
|
std::ofstream f(p, std::ios::binary | std::ios::trunc);
|
|
if (!f.is_open()) return false;
|
|
f.write(bytes.data(), (std::streamsize)bytes.size());
|
|
return f.good();
|
|
}
|
|
|
|
std::string read_file(const fs::path& p) {
|
|
std::ifstream f(p, std::ios::binary);
|
|
if (!f.is_open()) return "";
|
|
std::ostringstream ss;
|
|
ss << f.rdbuf();
|
|
return ss.str();
|
|
}
|
|
|
|
// Quote para shell: encierra en comillas dobles, escapa los " internos.
|
|
// Suficiente para paths del registry — no tocamos shell metacharacters
|
|
// porque no hay input de usuario directo (paths controlados por la app).
|
|
std::string sh_quote(const std::string& s) {
|
|
std::string out;
|
|
out.reserve(s.size() + 2);
|
|
out.push_back('"');
|
|
for (char c : s) {
|
|
if (c == '"' || c == '\\' || c == '$' || c == '`') {
|
|
out.push_back('\\');
|
|
}
|
|
out.push_back(c);
|
|
}
|
|
out.push_back('"');
|
|
return out;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
RunResult run_collector(const std::string& python_exe,
|
|
const std::string& run_py,
|
|
const std::string& tmp_dir,
|
|
const std::string& ctx_json) {
|
|
RunResult r;
|
|
|
|
std::error_code ec;
|
|
fs::create_directories(tmp_dir, ec);
|
|
if (ec) {
|
|
r.stderr_str = "create_directories failed: " + ec.message();
|
|
return r;
|
|
}
|
|
|
|
std::string uid = make_uid();
|
|
fs::path ctx_path = fs::path(tmp_dir) / ("ctx_" + uid + ".json");
|
|
fs::path out_path = fs::path(tmp_dir) / ("out_" + uid + ".json");
|
|
fs::path err_path = fs::path(tmp_dir) / ("err_" + uid + ".log");
|
|
|
|
if (!write_file(ctx_path, ctx_json)) {
|
|
r.stderr_str = "write ctx failed";
|
|
return r;
|
|
}
|
|
|
|
std::string cmd =
|
|
sh_quote(python_exe) + " " + sh_quote(run_py)
|
|
+ " < " + sh_quote(ctx_path.string())
|
|
+ " > " + sh_quote(out_path.string())
|
|
+ " 2> " + sh_quote(err_path.string());
|
|
|
|
long long t0 = now_ms();
|
|
int rc = std::system(cmd.c_str());
|
|
r.duration_ms = now_ms() - t0;
|
|
|
|
#if defined(__unix__) || defined(__APPLE__)
|
|
// POSIX: WEXITSTATUS para extraer exit code real.
|
|
if (rc != -1 && WIFEXITED(rc)) {
|
|
r.exit_code = WEXITSTATUS(rc);
|
|
} else {
|
|
r.exit_code = rc;
|
|
}
|
|
#else
|
|
r.exit_code = rc;
|
|
#endif
|
|
|
|
r.stdout_str = read_file(out_path);
|
|
r.stderr_str = read_file(err_path);
|
|
|
|
// Cleanup salvo si fallo (dejar para debug).
|
|
if (r.exit_code == 0) {
|
|
std::error_code rmec;
|
|
fs::remove(ctx_path, rmec);
|
|
fs::remove(out_path, rmec);
|
|
fs::remove(err_path, rmec);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
} // namespace odr
|