#include "runner.h" #include #include #include #include #include #include #include namespace fs = std::filesystem; namespace odr { namespace { long long now_ms() { using namespace std::chrono; return duration_cast( system_clock::now().time_since_epoch()).count(); } std::string make_uid() { static std::atomic 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