8#include "storage/ipc.h"
10#include <sys/socket.h>
36constexpr uint32_t CLIENT_RECV_MAX = 256u * 1024 * 1024;
40constexpr uint32_t CLIENT_SEND_MAX = 1u * 1024 * 1024;
43int connect_endpoint(
const std::string &endpoint)
45#ifdef PROVSQL_NO_SUBPROCESS
53 if (endpoint.rfind(
"unix:", 0) == 0) {
54 std::string path = endpoint.substr(5);
55 int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
58 struct sockaddr_un addr;
59 memset(&addr, 0,
sizeof(addr));
60 addr.sun_family = AF_UNIX;
61 if (path.size() >=
sizeof(addr.sun_path)) {
65 strncpy(addr.sun_path, path.c_str(),
sizeof(addr.sun_path) - 1);
66 if (::connect(fd,
reinterpret_cast<sockaddr *
>(&addr),
sizeof(addr)) < 0) {
73 auto colon = endpoint.rfind(
':');
74 if (colon == std::string::npos)
76 std::string host = endpoint.substr(0, colon), port = endpoint.substr(colon + 1);
77 struct addrinfo hints, *res =
nullptr;
78 memset(&hints, 0,
sizeof(hints));
79 hints.ai_family = AF_UNSPEC;
80 hints.ai_socktype = SOCK_STREAM;
81 if (::getaddrinfo(host.c_str(), port.c_str(), &hints, &res) != 0 || !res)
84 for (
auto *ai = res; ai; ai = ai->ai_next) {
85 fd = ::socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
88 if (::connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
98uint16_t get_u16(
const std::string &s,
size_t off)
100 return (uint16_t(
static_cast<unsigned char>(s[off])) << 8)
101 | uint16_t(
static_cast<unsigned char>(s[off + 1]));
107struct ServerError : std::runtime_error {
108 using std::runtime_error::runtime_error;
117std::string g_endpoint;
118uint32_t g_request_id = 0;
119bool g_atexit_registered =
false;
132void kcmcp_atexit(
int code, Datum arg)
137 unsigned char bye[10] = { 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
138 ssize_t n = ::write(g_fd, bye,
sizeof(bye));
151void wait_readable_or_cancel()
158 int r = ::poll(&pfd, 1, 100);
159 if (r > 0 && (pfd.revents & (POLLIN | POLLHUP | POLLERR)))
161 if (r < 0 && errno != EINTR)
163 if (QueryCancelPending || ProcDiePending) {
165 CHECK_FOR_INTERRUPTS();
174void ensure_connection(
const std::string &endpoint)
176 if (g_fd >= 0 && g_endpoint == endpoint)
180 int fd = connect_endpoint(endpoint);
182 throw std::runtime_error(
"cannot connect to KCMCP endpoint '" + endpoint +
"'");
184 Connection conn(fd, CLIENT_RECV_MAX, CLIENT_SEND_MAX);
185 conn.send(
Type::HELLO, 0,
"{\"kcmcp\":[1,0],\"client\":\"ProvSQL\"}");
188 throw std::runtime_error(
"KCMCP server closed during handshake");
190 throw std::runtime_error(
"KCMCP handshake refused: "
193 throw std::runtime_error(
"KCMCP: expected HELLO from server");
199 g_endpoint = endpoint;
203std::string do_compile(uint8_t input_format,
const std::string &problem)
205 Connection conn(g_fd, CLIENT_RECV_MAX, CLIENT_SEND_MAX);
208 req.push_back(
static_cast<char>(2));
209 req.push_back(
static_cast<char>(input_format));
210 req.push_back(
static_cast<char>(4));
221 wait_readable_or_cancel();
223 throw std::runtime_error(
"KCMCP server closed before RESULT");
227 uint16_t code = m.
payload.size() >= 2 ? get_u16(m.
payload, 0) : 0;
228 std::string msg = m.
payload.size() > 2 ? m.
payload.substr(2) :
"";
229 throw ServerError(
"KCMCP server error " + std::to_string(code)
234 throw std::runtime_error(
"KCMCP: unexpected frame type in reply");
239 throw std::runtime_error(
"KCMCP: truncated RESULT");
240 if (
static_cast<unsigned char>(m.
payload[0]) != 4)
241 throw std::runtime_error(
"KCMCP: server returned a non-ddnnf-nnf result");
242 uint16_t meta_len = get_u16(m.
payload, 2);
243 if (4u + meta_len > m.
payload.size())
244 throw std::runtime_error(
"KCMCP: malformed RESULT meta");
245 return m.
payload.substr(4 + meta_len);
253 const std::string &problem)
256 ::signal(SIGPIPE, SIG_IGN);
257 if (!g_atexit_registered) {
258 on_proc_exit(kcmcp_atexit, (Datum) 0);
259 g_atexit_registered =
true;
267 for (
int attempt = 0; ; ++attempt) {
268 bool reusing = (g_fd >= 0 && g_endpoint == endpoint);
270 ensure_connection(endpoint);
271 return do_compile(input_format, problem);
272 }
catch (
const ServerError &) {
274 }
catch (
const std::exception &) {
276 if (reusing && attempt == 0)
Framed message transport over one connected socket fd.
In-extension KCMCP client: compile a Boolean problem on a warm, socket-attached knowledge compiler in...
Wire codec for KCMCP, the Knowledge Compiler / Model Counter Protocol (see doc/source/dev/kc-server-p...
std::string kcmcp_compile(const std::string &endpoint, uint8_t input_format, const std::string &problem)
Compile problem on a KCMCP server and return its d-DNNF NNF text.
Build-configuration switches shared across the C and C++ sources.
A fully reassembled inbound message (MORE frames concatenated).