34 : parser([this](const
std::string& cmd) { execute(cmd); })
35 , commandController(commandController_)
36 , eventDistributor(eventDistributor_)
48void CliConnection::log(
CliComm::LogLevel level, std::string_view message)
noexcept
51 output(
tmpStrCat(
"<log level=\"", levelStr[level],
"\">",
56 std::string_view name, std::string_view value)
noexcept
58 if (!getUpdateEnable(type))
return;
61 auto tmp =
strCat(
"<update type=\"", updateStr[type],
'\"');
62 if (!machine.empty()) {
63 strAppend(tmp,
" machine=\"", machine,
'\"');
75 output(
"<openmsx-output>\n");
80 thread = std::thread([
this]() { run(); });
85 output(
"</openmsx-output>\n");
90 if (thread.joinable()) {
95void CliConnection::execute(
const std::string& command)
98 Event::create<CliCommandEvent>(command,
this));
103 return tmpStrCat(
"<reply result=\"", (status ?
"ok" :
"nok"),
"\">",
107int CliConnection::signalEvent(
const Event& event)
110 const auto& commandEvent = get<CliCommandEvent>(event);
111 if (commandEvent.getId() ==
this) {
114 commandEvent.getCommand(),
this).
getString();
115 output(reply(result,
true));
116 }
catch (CommandException&
e) {
117 std::string result = std::move(
e).getMessage() +
'\n';
118 output(reply(result,
false));
127static constexpr int BUF_SIZE = 4096;
140void StdioConnection::run()
149 std::array<char, BUF_SIZE> buf;
150 auto n = read(STDIN_FILENO, buf.data(),
sizeof(buf));
161 std::cout << message << std::flush;
164void StdioConnection::close()
175static const HANDLE OPENMSX_INVALID_HANDLE_VALUE =
reinterpret_cast<HANDLE
>(-1);
177PipeConnection::PipeConnection(CommandController& commandController_,
178 EventDistributor& eventDistributor_,
179 std::string_view name)
180 : CliConnection(commandController_, eventDistributor_)
182 auto pipeName =
strCat(
"\\\\.\\pipe\\", name);
183 pipeHandle = CreateFileA(pipeName.c_str(), GENERIC_READ, 0,
nullptr,
184 OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
nullptr);
185 if (pipeHandle == OPENMSX_INVALID_HANDLE_VALUE) {
186 throw FatalError(
"Error reopening pipefile '", pipeName,
"': error ",
187 unsigned(GetLastError()));
190 shutdownEvent = CreateEventW(
nullptr, FALSE, FALSE,
nullptr);
191 if (!shutdownEvent) {
192 throw FatalError(
"Error creating shutdown event: ", GetLastError());
198PipeConnection::~PipeConnection()
202 assert(pipeHandle == OPENMSX_INVALID_HANDLE_VALUE);
203 CloseHandle(shutdownEvent);
206static void InitOverlapped(LPOVERLAPPED overlapped)
208 ZeroMemory(overlapped,
sizeof(*overlapped));
209 overlapped->hEvent = CreateEventW(
nullptr, FALSE, FALSE,
nullptr);
210 if (!overlapped->hEvent) {
211 throw FatalError(
"Error creating overlapped event: ", GetLastError());
215static void ClearOverlapped(LPOVERLAPPED overlapped)
217 if (overlapped->hEvent) {
218 CloseHandle(overlapped->hEvent);
219 overlapped->hEvent =
nullptr;
223void PipeConnection::run()
226 OVERLAPPED overlapped;
227 InitOverlapped(&overlapped);
228 HANDLE waitHandles[2] = { shutdownEvent, overlapped.hEvent };
230 while (pipeHandle != OPENMSX_INVALID_HANDLE_VALUE) {
232 if (!ReadFile(pipeHandle, buf, BUF_SIZE,
nullptr, &overlapped) &&
233 GetLastError() != ERROR_IO_PENDING) {
236 DWORD wait = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
237 if (wait == WAIT_OBJECT_0 + 1) {
239 if (!GetOverlappedResult(pipeHandle, &overlapped, &bytesRead, TRUE)) {
242 parser.parse(std::span{buf, bytesRead});
243 }
else if (wait == WAIT_OBJECT_0) {
247 "WaitForMultipleObjects returned unexpectedly: ", wait);
251 ClearOverlapped(&overlapped);
254 CloseHandle(pipeHandle);
255 pipeHandle = OPENMSX_INVALID_HANDLE_VALUE;
258void PipeConnection::output(std::string_view message)
260 if (pipeHandle != OPENMSX_INVALID_HANDLE_VALUE) {
261 std::cout << message << std::flush;
265void PipeConnection::close()
267 SetEvent(shutdownEvent);
287void SocketConnection::run()
293 std::lock_guard<std::mutex> lock(sdMutex);
295 SocketStreamWrapper stream(sd);
296 SspiNegotiateServer server(stream);
297 ok = server.Authenticate() && server.Authorize();
318 std::array<char, BUF_SIZE> buf;
319 auto n =
sock_recv(sd, buf.data(),
sizeof(buf));
337 std::span message{message_.begin(), message_.end()};
338 while (!message.empty()) {
341 std::lock_guard<std::mutex> lock(sdMutex);
343 bytesSend =
sock_send(sd, message.data(), message.size());
346 message = message.subspan(bytesSend);
358void SocketConnection::closeSocket()
360 std::lock_guard<std::mutex> lock(sdMutex);
368void SocketConnection::close()
void XMLEscape(std::string_view s, Output output)
void parse(std::span< const char > buf)
static std::span< const std::string_view, NUM_LEVELS > getLevelStrings()
static std::span< const std::string_view, NUM_UPDATES > getUpdateStrings()
void end()
End this connection by sending the closing tag and then closing the stream.
~CliConnection() override
virtual void close()=0
Close the connection.
void start()
Starts the helper thread.
AdhocCliCommParser parser
void startOutput()
Send opening XML tag, should be called exactly once by a subclass shortly after opening a connection.
virtual void output(std::string_view message)=0
CliConnection(CommandController &commandController, EventDistributor &eventDistributor)
virtual TclObject executeCommand(zstring_view command, CliConnection *connection=nullptr)=0
Execute the given command.
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
bool poll(int fd)
Waits for an event to occur on the given file descriptor.
bool aborted()
Returns true iff abort() was called.
void abort()
Aborts a poll in progress and any future poll attempts.
SocketConnection(CommandController &commandController, EventDistributor &eventDistributor, SOCKET sd)
~SocketConnection() override
void output(std::string_view message) override
void output(std::string_view message) override
~StdioConnection() override
StdioConnection(CommandController &commandController, EventDistributor &eventDistributor)
zstring_view getString() const
This file implemented 3 utility functions:
constexpr int OPENMSX_INVALID_SOCKET
ptrdiff_t sock_send(SOCKET sd, const char *buf, size_t count)
void sock_close(SOCKET sd)
EventType getType(const Event &event)
ptrdiff_t sock_recv(SOCKET sd, char *buf, size_t count)
constexpr void fill(ForwardRange &&range, const T &value)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
TemporaryString tmpStrCat(Ts &&... ts)
std::string strCat(Ts &&...ts)
void strAppend(std::string &result, Ts &&...ts)
constexpr auto end(const zstring_view &x)