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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ else()
message(STATUS "idasql: Fetching libxsql from GitHub...")
FetchContent_Declare(libxsql
GIT_REPOSITORY https://github.com/0xeb/libxsql.git
GIT_TAG v1.0.8
GIT_TAG v1.0.10
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(libxsql)
Expand Down
51 changes: 51 additions & 0 deletions src/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,58 @@ static std::string build_cli_http_help_text() {
return out.str();
}

// CLI --http server, on the shared libxsql thinclient (use_queue=true: queries
// run on this main thread via run_until_stopped, for Hex-Rays thread affinity).
static int run_http_mode(idasql::Database& db, int port, const std::string& bind_addr, const std::string& auth_token) {
idasql::IDAHTTPServer server;
idasql::HTTPStatementExecutor exec =
[&db](const std::string& stmt, xsql::ScriptStatementResult& out) {
idasql::QueryResult r = db.query(stmt);
out.columns = r.columns;
out.rows.reserve(r.rows.size());
for (const auto& row : r.rows) out.rows.push_back(row.values);
out.elapsed_ms = static_cast<double>(r.elapsed_ms);
out.success = r.success;
out.error = r.error;
};

int actual_port = server.start(port, exec, bind_addr, /*use_queue=*/true, auth_token);
if (actual_port < 0) {
std::cerr << "Error: Failed to start HTTP server\n";
return 1;
}

g_http_stop_requested.store(false);
auto old_handler = std::signal(SIGINT, http_signal_handler);
#ifdef _WIN32
auto old_break = std::signal(SIGBREAK, http_signal_handler);
#else
auto old_term = std::signal(SIGTERM, http_signal_handler);
#endif
server.set_interrupt_check([]() { return g_http_stop_requested.load(); });

std::cout << "IDASQL HTTP server: http://" << (bind_addr.empty() ? "127.0.0.1" : bind_addr)
<< ":" << actual_port << "\n";
std::cout << "Database: " << db.info() << "\n";
std::cout << "Press Ctrl+C to stop.\n\n";
std::cout.flush();

server.run_until_stopped();
server.stop();

std::signal(SIGINT, old_handler);
#ifdef _WIN32
std::signal(SIGBREAK, old_break);
#else
std::signal(SIGTERM, old_term);
#endif
std::cout << "\nHTTP server stopped.\n";
return 0;
}

// Superseded by the thinclient-based run_http_mode above; retained briefly and
// no longer called (cleanup follow-up).
static int run_http_mode_legacy(idasql::Database& db, int port, const std::string& bind_addr, const std::string& auth_token) {
xsql::thinclient::server_config cfg;
cfg.port = port;
cfg.bind_address = bind_addr.empty() ? "127.0.0.1" : bind_addr;
Expand Down
3 changes: 3 additions & 0 deletions src/common/http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ int IDAHTTPServer::start(int port, HTTPStatementExecutor executor,
bind_addr_ = bind_addr.empty() ? "127.0.0.1" : bind_addr;
auto config = make_idasql_config(port, bind_addr_, use_queue);
config.statement_executor = std::move(executor);
// Non-queue servers (REPL/plugin background) run the executor on the HTTP
// worker; serialize so the non-concurrency-safe IDA DB handle is safe.
config.serialize_requests = !use_queue;
if (!auth_token.empty()) config.auth_token = auth_token;
impl_ = std::make_unique<xsql::thinclient::http_query_server>(config);
return impl_->start();
Expand Down
26 changes: 19 additions & 7 deletions src/plugin/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,14 +358,26 @@ struct idasql_plugmod_t : public plugmod_t
}
}

// SQL executor that uses execute_sync for thread safety and returns JSON
idasql::HTTPQueryCallback sql_cb = [this](const std::string& sql) -> std::string {
xsql::ScriptResult result = run_query_script_sync(sql);
return xsql::script_result_to_json(result);
};
// Single-statement executor: each statement runs on IDA's main thread
// via execute_sync (Hex-Rays thread affinity). The thinclient owns
// multi-statement orchestration, options, and formatting; non-queue +
// serialize_requests keeps requests one-at-a-time.
idasql::HTTPStatementExecutor sql_exec =
[this](const std::string& stmt, xsql::ScriptStatementResult& out) {
query_request_t req(engine_.get(), stmt);
execute_sync(req, MFF_WRITE);
const idasql::QueryResult& r = req.result;
out.columns = r.columns;
out.rows.reserve(r.rows.size());
for (const auto& row : r.rows) out.rows.push_back(row.values);
out.elapsed_ms = static_cast<double>(r.elapsed_ms);
out.success = r.success;
out.error = r.error;
};

// Start HTTP server, no queue (plugin mode)
int port = http_server_.start(req_port, sql_cb, addr);
// Start HTTP server, no queue (plugin mode; execute_sync marshals each
// statement to the main thread).
int port = http_server_.start(req_port, sql_exec, addr, /*use_queue=*/false);
if (port <= 0) {
return "Error: Failed to start HTTP server";
}
Expand Down
Loading