koffi 2.14.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -1
- package/README.md +1 -1
- package/build/koffi/darwin_arm64/koffi.node +0 -0
- package/build/koffi/darwin_x64/koffi.node +0 -0
- package/build/koffi/freebsd_arm64/koffi.node +0 -0
- package/build/koffi/freebsd_ia32/koffi.node +0 -0
- package/build/koffi/freebsd_x64/koffi.node +0 -0
- package/build/koffi/linux_arm64/koffi.node +0 -0
- package/build/koffi/linux_armhf/koffi.node +0 -0
- package/build/koffi/linux_ia32/koffi.node +0 -0
- package/build/koffi/linux_loong64/koffi.node +0 -0
- package/build/koffi/linux_riscv64d/koffi.node +0 -0
- package/build/koffi/linux_x64/koffi.node +0 -0
- package/build/koffi/musl_arm64/koffi.node +0 -0
- package/build/koffi/musl_x64/koffi.node +0 -0
- package/build/koffi/openbsd_ia32/koffi.node +0 -0
- package/build/koffi/openbsd_x64/koffi.node +0 -0
- package/build/koffi/win32_arm64/koffi.node +0 -0
- package/build/koffi/win32_ia32/koffi.node +0 -0
- package/build/koffi/win32_x64/koffi.node +0 -0
- package/doc/assets.ini +2 -1
- package/doc/build.sh +9 -0
- package/doc/pages/404.md +17 -0
- package/doc/pages/index.md +43 -4
- package/doc/pages/misc.md +16 -11
- package/doc/pages/platforms.md +8 -19
- package/doc/pages.ini +4 -7
- package/doc/static/highlight.js +2 -14
- package/doc/static/koffi.css +3 -15
- package/doc/static/perf_windows.png +0 -0
- package/doc/static/print.css +2 -14
- package/index.d.ts +29 -24
- package/index.js +10 -9
- package/indirect.js +10 -9
- package/{src/core → lib/native}/base/base.cc +1753 -1089
- package/{src/core → lib/native}/base/base.hh +868 -572
- package/{src/core → lib/native}/base/crc.inc +3 -21
- package/lib/native/base/crc_gen.py +72 -0
- package/{src/core → lib/native}/base/mimetypes.inc +2 -20
- package/{src/core → lib/native}/base/mimetypes_gen.py +2 -21
- package/lib/native/base/tower.cc +821 -0
- package/lib/native/base/tower.hh +81 -0
- package/{src/core → lib/native}/base/unicode.inc +3 -21
- package/{src/core → lib/native}/base/unicode_gen.py +5 -42
- package/package.json +3 -2
- package/src/cnoke/assets/FindCNoke.cmake +8 -20
- package/src/cnoke/assets/win_delay_hook.c +2 -20
- package/src/cnoke/cnoke.js +2 -21
- package/src/cnoke/src/builder.js +3 -22
- package/src/cnoke/src/index.js +2 -20
- package/src/cnoke/src/tools.js +2 -20
- package/src/koffi/CMakeLists.txt +19 -22
- package/src/koffi/cmake/raylib.cmake +5 -22
- package/src/koffi/cmake/sqlite3.cmake +2 -20
- package/src/koffi/src/abi_arm32.cc +31 -49
- package/src/koffi/src/abi_arm32_asm.S +2 -20
- package/src/koffi/src/abi_arm64.cc +36 -54
- package/src/koffi/src/abi_arm64_asm.S +2 -20
- package/src/koffi/src/abi_arm64_asm.asm +2 -20
- package/src/koffi/src/abi_loong64.cc +2 -20
- package/src/koffi/src/abi_loong64_asm.S +2 -20
- package/src/koffi/src/abi_riscv64.cc +34 -52
- package/src/koffi/src/abi_riscv64_asm.S +2 -20
- package/src/koffi/src/abi_x64_sysv.cc +36 -54
- package/src/koffi/src/abi_x64_sysv_asm.S +2 -20
- package/src/koffi/src/abi_x64_win.cc +32 -50
- package/src/koffi/src/abi_x64_win_asm.asm +2 -20
- package/src/koffi/src/abi_x86.cc +33 -51
- package/src/koffi/src/abi_x86_asm.S +2 -20
- package/src/koffi/src/abi_x86_asm.asm +2 -20
- package/src/koffi/src/call.cc +107 -281
- package/src/koffi/src/call.hh +9 -27
- package/src/koffi/src/errno.inc +2 -20
- package/src/koffi/src/ffi.cc +121 -121
- package/src/koffi/src/ffi.hh +23 -38
- package/src/koffi/src/init.js +2 -20
- package/src/koffi/src/parser.cc +15 -29
- package/src/koffi/src/parser.hh +4 -22
- package/src/koffi/src/trampolines/armasm.inc +0 -21
- package/src/koffi/src/trampolines/gnu.inc +0 -21
- package/src/koffi/src/trampolines/masm32.inc +0 -21
- package/src/koffi/src/trampolines/masm64.inc +0 -21
- package/src/koffi/src/trampolines/prototypes.inc +1 -22
- package/src/koffi/src/util.cc +87 -102
- package/src/koffi/src/util.hh +11 -29
- package/src/koffi/src/uv.cc +193 -0
- package/src/koffi/src/uv.def +10 -0
- package/src/koffi/src/uv.hh +40 -0
- package/src/koffi/src/win32.cc +7 -25
- package/src/koffi/src/win32.hh +4 -22
- package/vendor/node-api-headers/include/uv/aix.h +32 -0
- package/vendor/node-api-headers/include/uv/bsd.h +34 -0
- package/vendor/node-api-headers/include/uv/darwin.h +61 -0
- package/vendor/node-api-headers/include/uv/errno.h +483 -0
- package/vendor/node-api-headers/include/uv/linux.h +34 -0
- package/vendor/node-api-headers/include/uv/os390.h +33 -0
- package/vendor/node-api-headers/include/uv/posix.h +31 -0
- package/vendor/node-api-headers/include/uv/sunos.h +44 -0
- package/vendor/node-api-headers/include/uv/threadpool.h +37 -0
- package/vendor/node-api-headers/include/uv/tree.h +521 -0
- package/vendor/node-api-headers/include/uv/unix.h +512 -0
- package/vendor/node-api-headers/include/uv/version.h +43 -0
- package/vendor/node-api-headers/include/uv/win.h +698 -0
- package/vendor/node-api-headers/include/uv.h +1990 -0
- package/src/core/base/crc_gen.py +0 -109
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Niels Martignène <niels.martignene@protonmail.com>
|
|
3
|
+
|
|
4
|
+
#include "base.hh"
|
|
5
|
+
#include "tower.hh"
|
|
6
|
+
|
|
7
|
+
#if defined(_WIN32)
|
|
8
|
+
#if !defined(NOMINMAX)
|
|
9
|
+
#define NOMINMAX
|
|
10
|
+
#endif
|
|
11
|
+
#if !defined(WIN32_LEAN_AND_MEAN)
|
|
12
|
+
#define WIN32_LEAN_AND_MEAN
|
|
13
|
+
#endif
|
|
14
|
+
#define SECURITY_WIN32
|
|
15
|
+
#include <windows.h>
|
|
16
|
+
#include <security.h>
|
|
17
|
+
#else
|
|
18
|
+
#include <fcntl.h>
|
|
19
|
+
#include <poll.h>
|
|
20
|
+
#include <sys/socket.h>
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
namespace K {
|
|
24
|
+
|
|
25
|
+
#if defined(_WIN32)
|
|
26
|
+
|
|
27
|
+
struct OverlappedPipe {
|
|
28
|
+
OVERLAPPED ov = {};
|
|
29
|
+
HANDLE h = nullptr;
|
|
30
|
+
uint8_t buf[1024];
|
|
31
|
+
|
|
32
|
+
~OverlappedPipe();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
OverlappedPipe::~OverlappedPipe()
|
|
36
|
+
{
|
|
37
|
+
if (h) {
|
|
38
|
+
CancelIo(h);
|
|
39
|
+
CloseHandle(h);
|
|
40
|
+
}
|
|
41
|
+
if (ov.hEvent) {
|
|
42
|
+
CloseHandle(ov.hEvent);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static bool CheckPipePath(const char *path)
|
|
47
|
+
{
|
|
48
|
+
if (!StartsWith(path, "\\\\.\\pipe\\")) {
|
|
49
|
+
LogError("Control pipe names must start with '%1'", "\\\\.\\pipe\\");
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (!path[9]) {
|
|
53
|
+
LogError("Truncated control pipe name '%1'", path);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static OverlappedPipe *BindPipe(const char *path)
|
|
61
|
+
{
|
|
62
|
+
OverlappedPipe *pipe = new OverlappedPipe();
|
|
63
|
+
K_DEFER_N(err_guard) { delete pipe; };
|
|
64
|
+
|
|
65
|
+
pipe->ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
66
|
+
if (!pipe->ov.hEvent) {
|
|
67
|
+
LogError("Failed to create event: %1", GetWin32ErrorString());
|
|
68
|
+
return nullptr;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pipe->h = CreateNamedPipeA(path, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
72
|
+
PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 8192, 8192, 0, nullptr);
|
|
73
|
+
if (pipe->h == INVALID_HANDLE_VALUE) {
|
|
74
|
+
pipe->h = nullptr;
|
|
75
|
+
|
|
76
|
+
LogError("Failed to create named control pipe: %1", GetWin32ErrorString());
|
|
77
|
+
return nullptr;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (ConnectNamedPipe(pipe->h, &pipe->ov) || GetLastError() == ERROR_PIPE_CONNECTED) {
|
|
81
|
+
SetEvent(pipe->ov.hEvent);
|
|
82
|
+
} else if (GetLastError() != ERROR_IO_PENDING) {
|
|
83
|
+
LogError("Failed to connect to named pipe: %1", GetWin32ErrorString());
|
|
84
|
+
return nullptr;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
err_guard.Disable();
|
|
88
|
+
return pipe;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static OverlappedPipe *ConnectPipe(const char *path)
|
|
92
|
+
{
|
|
93
|
+
OverlappedPipe *pipe = new OverlappedPipe();
|
|
94
|
+
K_DEFER_N(err_guard) { delete pipe; };
|
|
95
|
+
|
|
96
|
+
pipe->ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
97
|
+
if (!pipe->ov.hEvent) {
|
|
98
|
+
LogError("Failed to create event: %1", GetWin32ErrorString());
|
|
99
|
+
return nullptr;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (int i = 0; i < 10; i++) {
|
|
103
|
+
if (!WaitNamedPipeA(path, 10))
|
|
104
|
+
continue;
|
|
105
|
+
|
|
106
|
+
pipe->h = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
|
|
107
|
+
0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
|
|
108
|
+
|
|
109
|
+
if (pipe->h != INVALID_HANDLE_VALUE)
|
|
110
|
+
break;
|
|
111
|
+
pipe->h = nullptr;
|
|
112
|
+
|
|
113
|
+
if (GetLastError() != ERROR_PIPE_BUSY) {
|
|
114
|
+
LogError("Failed to connect to named pipe: %1", GetWin32ErrorString());
|
|
115
|
+
return nullptr;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!pipe->h) {
|
|
120
|
+
LogError("Failed to connect to named pipe: %1", GetWin32ErrorString());
|
|
121
|
+
return nullptr;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
err_guard.Disable();
|
|
125
|
+
return pipe;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Does not print errors
|
|
129
|
+
static bool StartRead(OverlappedPipe *pipe)
|
|
130
|
+
{
|
|
131
|
+
ResetEvent(pipe->ov.hEvent);
|
|
132
|
+
|
|
133
|
+
if (!::ReadFile(pipe->h, pipe->buf, K_SIZE(pipe->buf), nullptr, &pipe->ov) &&
|
|
134
|
+
GetLastError() != ERROR_IO_PENDING)
|
|
135
|
+
return false;
|
|
136
|
+
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Does not print errors
|
|
141
|
+
static Size FinalizeRead(OverlappedPipe *pipe)
|
|
142
|
+
{
|
|
143
|
+
DWORD len = 0;
|
|
144
|
+
if (!GetOverlappedResult(pipe->h, &pipe->ov, &len, TRUE))
|
|
145
|
+
return -1;
|
|
146
|
+
|
|
147
|
+
return len;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Does not print errors
|
|
151
|
+
static Size ReadSync(OverlappedPipe *pipe, void *buf, Size buf_len, int timeout)
|
|
152
|
+
{
|
|
153
|
+
DWORD len = 0;
|
|
154
|
+
|
|
155
|
+
if (!::ReadFile(pipe->h, buf, (DWORD)buf_len, nullptr, &pipe->ov) &&
|
|
156
|
+
GetLastError() != ERROR_IO_PENDING)
|
|
157
|
+
return -1;
|
|
158
|
+
if (timeout > 0)
|
|
159
|
+
WaitForSingleObject(pipe->ov.hEvent, timeout);
|
|
160
|
+
if (!GetOverlappedResult(pipe->h, &pipe->ov, &len, timeout < 0) &&
|
|
161
|
+
GetLastError() != ERROR_IO_INCOMPLETE)
|
|
162
|
+
return -1;
|
|
163
|
+
|
|
164
|
+
return (Size)len;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Does not print errors
|
|
168
|
+
static Size WriteSync(OverlappedPipe *pipe, const void *buf, Size buf_len)
|
|
169
|
+
{
|
|
170
|
+
OVERLAPPED ov = {};
|
|
171
|
+
DWORD written = 0;
|
|
172
|
+
|
|
173
|
+
if (!::WriteFile(pipe->h, buf, (DWORD)buf_len, nullptr, &ov) &&
|
|
174
|
+
GetLastError() != ERROR_IO_PENDING)
|
|
175
|
+
return -1;
|
|
176
|
+
if (!GetOverlappedResult(pipe->h, &ov, &written, TRUE))
|
|
177
|
+
return -1;
|
|
178
|
+
|
|
179
|
+
return (Size)written;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
bool TowerServer::Bind(const char *path)
|
|
183
|
+
{
|
|
184
|
+
K_ASSERT(!name[0]);
|
|
185
|
+
K_ASSERT(!pipes.len);
|
|
186
|
+
|
|
187
|
+
K_DEFER_N(err_guard) { Stop(); };
|
|
188
|
+
|
|
189
|
+
if (!CheckPipePath(path))
|
|
190
|
+
return false;
|
|
191
|
+
if (!CopyString(path, name)) {
|
|
192
|
+
LogError("Control pipe name '%1' is too long", path);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
OverlappedPipe *pipe = BindPipe(path);
|
|
197
|
+
if (!pipe)
|
|
198
|
+
return false;
|
|
199
|
+
pipes.Append(pipe);
|
|
200
|
+
|
|
201
|
+
err_guard.Disable();
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
void TowerServer::Start(std::function<bool(StreamReader *, StreamWriter *)> func)
|
|
206
|
+
{
|
|
207
|
+
K_ASSERT(pipes.len == 1);
|
|
208
|
+
K_ASSERT(!sources.len);
|
|
209
|
+
K_ASSERT(!handle_func);
|
|
210
|
+
|
|
211
|
+
sources.Append({ pipes[0]->ov.hEvent, -1 });
|
|
212
|
+
handle_func = func;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void TowerServer::Stop()
|
|
216
|
+
{
|
|
217
|
+
for (OverlappedPipe *pipe: pipes) {
|
|
218
|
+
delete pipe;
|
|
219
|
+
}
|
|
220
|
+
pipes.Clear();
|
|
221
|
+
sources.Clear();
|
|
222
|
+
|
|
223
|
+
MemSet(name, 0, K_SIZE(name));
|
|
224
|
+
|
|
225
|
+
handle_func = {};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static bool IsSignaled(HANDLE h)
|
|
229
|
+
{
|
|
230
|
+
DWORD ret = WaitForSingleObject(h, 0);
|
|
231
|
+
return ret == WAIT_OBJECT_0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
bool TowerServer::Process(uint64_t ready)
|
|
235
|
+
{
|
|
236
|
+
// Accept new clients
|
|
237
|
+
if (ready & 1) {
|
|
238
|
+
OverlappedPipe *client = pipes[0];
|
|
239
|
+
|
|
240
|
+
if (IsSignaled(client->ov.hEvent)) {
|
|
241
|
+
OverlappedPipe *pipe = BindPipe(name);
|
|
242
|
+
|
|
243
|
+
// We're kind of screwed if this happens, let the caller know and fail hard
|
|
244
|
+
if (!pipe) {
|
|
245
|
+
sources.len = 0;
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
pipes[0] = pipe;
|
|
250
|
+
sources[0].handle = pipe->ov.hEvent;
|
|
251
|
+
|
|
252
|
+
if (pipes.Available()) [[likely]] {
|
|
253
|
+
if (FinalizeRead(client) == 0 && StartRead(client)) {
|
|
254
|
+
pipes.Append(client);
|
|
255
|
+
sources.Append({ client->ov.hEvent, -1 });
|
|
256
|
+
|
|
257
|
+
LogDebug("Client has connected");
|
|
258
|
+
} else {
|
|
259
|
+
LogError("Failed to accept client: %1", GetWin32ErrorString());
|
|
260
|
+
delete client;
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
LogError("Too many connections, refusing new client");
|
|
264
|
+
delete client;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
RunClients([&](Size idx, OverlappedPipe *pipe) {
|
|
270
|
+
if (!(ready & (1ull << idx)))
|
|
271
|
+
return true;
|
|
272
|
+
|
|
273
|
+
Span<uint8_t> buf = MakeSpan(pipe->buf, FinalizeRead(pipe));
|
|
274
|
+
if (buf.len < 0) {
|
|
275
|
+
LogDebug("Client has disconnected");
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const auto read = [&](Span<uint8_t> out_buf) {
|
|
280
|
+
if (buf.len) {
|
|
281
|
+
Size copy_len = std::min(buf.len, out_buf.len);
|
|
282
|
+
MemCpy(out_buf.ptr, buf.ptr, copy_len);
|
|
283
|
+
|
|
284
|
+
buf.ptr += copy_len;
|
|
285
|
+
buf.len -= copy_len;
|
|
286
|
+
|
|
287
|
+
return copy_len;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
Size received = ReadSync(pipe, out_buf.ptr, out_buf.len, 1000);
|
|
291
|
+
if (received < 0) {
|
|
292
|
+
LogError("Failed to receive data from client: %1", GetWin32ErrorString());
|
|
293
|
+
} else if (!received) {
|
|
294
|
+
LogError("Client has timed out");
|
|
295
|
+
received = -1;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return received;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const auto write = [&](Span<const uint8_t> buf) {
|
|
302
|
+
while (buf.len) {
|
|
303
|
+
Size sent = WriteSync(pipe, buf.ptr, buf.len);
|
|
304
|
+
if (sent < 0) {
|
|
305
|
+
LogError("Failed to send data to server: %1", GetWin32ErrorString());
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
buf.ptr += sent;
|
|
310
|
+
buf.len -= sent;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return true;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
StreamReader reader(read, "<client>");
|
|
317
|
+
StreamWriter writer(write, "<client>");
|
|
318
|
+
|
|
319
|
+
if (!handle_func(&reader, &writer))
|
|
320
|
+
return false;
|
|
321
|
+
if (!reader.Close())
|
|
322
|
+
return false;
|
|
323
|
+
if (!writer.Close())
|
|
324
|
+
return false;
|
|
325
|
+
|
|
326
|
+
if (!StartRead(pipe)) {
|
|
327
|
+
LogDebug("Client has disconnected");
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return true;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
void TowerServer::Send(FunctionRef<void(StreamWriter *)> func)
|
|
338
|
+
{
|
|
339
|
+
RunClients([&](Size, OverlappedPipe *pipe) {
|
|
340
|
+
const auto write = [&](Span<const uint8_t> buf) {
|
|
341
|
+
while (buf.len) {
|
|
342
|
+
Size sent = WriteSync(pipe, buf.ptr, buf.len);
|
|
343
|
+
if (sent < 0) {
|
|
344
|
+
LogError("Failed to send data to server: %1", GetWin32ErrorString());
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
buf.ptr += sent;
|
|
349
|
+
buf.len -= sent;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return true;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
StreamWriter writer(write, "<client>");
|
|
356
|
+
func(&writer);
|
|
357
|
+
|
|
358
|
+
return writer.Close();
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
void TowerServer::RunClients(FunctionRef<bool(Size, OverlappedPipe *)> func)
|
|
363
|
+
{
|
|
364
|
+
Size j = 1;
|
|
365
|
+
for (Size i = 1; i < pipes.len; i++) {
|
|
366
|
+
OverlappedPipe *pipe = pipes[i];
|
|
367
|
+
|
|
368
|
+
pipes[j] = pipe;
|
|
369
|
+
sources[j].handle = pipe->ov.hEvent;
|
|
370
|
+
|
|
371
|
+
if (!func(i, pipe)) {
|
|
372
|
+
delete pipe;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
j++;
|
|
377
|
+
}
|
|
378
|
+
pipes.len = j;
|
|
379
|
+
sources.len = j;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
bool TowerClient::Connect(const char *path)
|
|
383
|
+
{
|
|
384
|
+
Stop();
|
|
385
|
+
|
|
386
|
+
K_DEFER_N(err_guard) { Stop(); };
|
|
387
|
+
|
|
388
|
+
if (!CheckPipePath(path))
|
|
389
|
+
return false;
|
|
390
|
+
|
|
391
|
+
pipe = ConnectPipe(path);
|
|
392
|
+
if (!pipe)
|
|
393
|
+
return false;
|
|
394
|
+
|
|
395
|
+
if (!StartRead(pipe)) {
|
|
396
|
+
LogError("Failed to connect to named pipe: %1", GetWin32ErrorString());
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
err_guard.Disable();
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
void TowerClient::Start(std::function<void(StreamReader *)> func)
|
|
405
|
+
{
|
|
406
|
+
K_ASSERT(pipe);
|
|
407
|
+
K_ASSERT(!handle_func);
|
|
408
|
+
|
|
409
|
+
src = { pipe->ov.hEvent, -1 };
|
|
410
|
+
handle_func = func;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
void TowerClient::Stop()
|
|
414
|
+
{
|
|
415
|
+
if (pipe) {
|
|
416
|
+
delete pipe;
|
|
417
|
+
pipe = nullptr;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
handle_func = {};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
bool TowerClient::Process()
|
|
424
|
+
{
|
|
425
|
+
if (!IsSignaled(pipe->ov.hEvent))
|
|
426
|
+
return true;
|
|
427
|
+
|
|
428
|
+
Span<uint8_t> buf = MakeSpan(pipe->buf, FinalizeRead(pipe));
|
|
429
|
+
if (buf.len < 0) {
|
|
430
|
+
LogError("Lost connection to server");
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const auto read = [&](Span<uint8_t> out_buf) {
|
|
435
|
+
if (buf.len) {
|
|
436
|
+
Size copy_len = std::min(buf.len, out_buf.len);
|
|
437
|
+
MemCpy(out_buf.ptr, buf.ptr, copy_len);
|
|
438
|
+
|
|
439
|
+
buf.ptr += copy_len;
|
|
440
|
+
buf.len -= copy_len;
|
|
441
|
+
|
|
442
|
+
return copy_len;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
Size received = ReadSync(pipe, out_buf.ptr, out_buf.len, -1);
|
|
446
|
+
if (received < 0) {
|
|
447
|
+
LogError("Failed to receive data from server: %1", strerror(errno));
|
|
448
|
+
}
|
|
449
|
+
return received;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
StreamReader reader(read, "<client>");
|
|
453
|
+
handle_func(&reader);
|
|
454
|
+
|
|
455
|
+
if (!reader.Close())
|
|
456
|
+
return false;
|
|
457
|
+
|
|
458
|
+
if (!StartRead(pipe)) {
|
|
459
|
+
LogError("Lost connection to server");
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
bool TowerClient::Send(FunctionRef<void(StreamWriter *)> func)
|
|
467
|
+
{
|
|
468
|
+
const auto write = [&](Span<const uint8_t> buf) {
|
|
469
|
+
while (buf.len) {
|
|
470
|
+
Size sent = WriteSync(pipe, buf.ptr, buf.len);
|
|
471
|
+
if (sent < 0) {
|
|
472
|
+
LogError("Failed to send data to server: %1", GetWin32ErrorString());
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
buf.ptr += sent;
|
|
477
|
+
buf.len -= sent;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return true;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
StreamWriter writer(write, "<server>");
|
|
484
|
+
func(&writer);
|
|
485
|
+
|
|
486
|
+
return writer.Close();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const char *GetControlSocketPath(ControlScope scope, const char *name, Allocator *alloc)
|
|
490
|
+
{
|
|
491
|
+
K_ASSERT(strlen(name) < 64);
|
|
492
|
+
|
|
493
|
+
switch (scope) {
|
|
494
|
+
case ControlScope::System: return Fmt(alloc, "\\\\.\\pipe\\tower\\system\\%1", name).ptr;
|
|
495
|
+
|
|
496
|
+
case ControlScope::User: {
|
|
497
|
+
char buf[128] = {};
|
|
498
|
+
|
|
499
|
+
ULONG size = K_SIZE(buf);
|
|
500
|
+
BOOL success = GetUserNameExA(NameUniqueId, buf, &size);
|
|
501
|
+
K_CRITICAL(success, "Failed to get user name");
|
|
502
|
+
|
|
503
|
+
Span<const char> uuid = MakeSpan(buf, size);
|
|
504
|
+
return Fmt(alloc, "\\\\.\\pipe\\tower\\%1\\%2", TrimStr(uuid, "{}"), name).ptr;
|
|
505
|
+
} break;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
K_UNREACHABLE();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
#else
|
|
512
|
+
|
|
513
|
+
bool TowerServer::Bind(const char *path)
|
|
514
|
+
{
|
|
515
|
+
K_ASSERT(fd < 0);
|
|
516
|
+
|
|
517
|
+
K_DEFER_N(err_guard) { Stop(); };
|
|
518
|
+
|
|
519
|
+
fd = CreateSocket(SocketType::Unix, SOCK_STREAM);
|
|
520
|
+
if (fd < 0)
|
|
521
|
+
return false;
|
|
522
|
+
SetDescriptorNonBlock(fd, true);
|
|
523
|
+
|
|
524
|
+
if (!BindUnixSocket(fd, path))
|
|
525
|
+
return false;
|
|
526
|
+
if (listen(fd, 4) < 0) {
|
|
527
|
+
LogError("listen() failed: %1", strerror(errno));
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
err_guard.Disable();
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
void TowerServer::Start(std::function<bool(StreamReader *, StreamWriter *)> func)
|
|
536
|
+
{
|
|
537
|
+
K_ASSERT(fd >= 0);
|
|
538
|
+
K_ASSERT(!handle_func);
|
|
539
|
+
|
|
540
|
+
sources.Append({ fd, -1 });
|
|
541
|
+
handle_func = func;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
void TowerServer::Stop()
|
|
545
|
+
{
|
|
546
|
+
if (fd >= 0) {
|
|
547
|
+
CloseDescriptor(fd);
|
|
548
|
+
fd = -1;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
for (Size i = 1; i < sources.len; i++) {
|
|
552
|
+
CloseDescriptor(sources[i].fd);
|
|
553
|
+
}
|
|
554
|
+
sources.Clear();
|
|
555
|
+
|
|
556
|
+
handle_func = {};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
static bool IsReadable(int fd, int timeout)
|
|
560
|
+
{
|
|
561
|
+
struct pollfd pfd = { fd, POLLIN, 0 };
|
|
562
|
+
|
|
563
|
+
if (poll(&pfd, 1, timeout) < 0)
|
|
564
|
+
return true;
|
|
565
|
+
if (pfd.revents)
|
|
566
|
+
return true;
|
|
567
|
+
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
bool TowerServer::Process(uint64_t ready)
|
|
572
|
+
{
|
|
573
|
+
// Accept new clients
|
|
574
|
+
if (ready & 1) {
|
|
575
|
+
#if defined(SOCK_CLOEXEC)
|
|
576
|
+
int sock = accept4(fd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
|
577
|
+
#else
|
|
578
|
+
int sock = accept(fd, nullptr, nullptr);
|
|
579
|
+
#endif
|
|
580
|
+
|
|
581
|
+
if (sock >= 0) {
|
|
582
|
+
#if !defined(SOCK_CLOEXEC)
|
|
583
|
+
fcntl(sock, F_SETFD, FD_CLOEXEC);
|
|
584
|
+
#endif
|
|
585
|
+
#if !defined(MSG_DONTWAIT)
|
|
586
|
+
SetDescriptorNonBlock(sock, true);
|
|
587
|
+
#endif
|
|
588
|
+
|
|
589
|
+
if (sources.Available()) [[likely]] {
|
|
590
|
+
sources.Append({ sock, -1 });
|
|
591
|
+
LogDebug("Client has connected");
|
|
592
|
+
} else {
|
|
593
|
+
LogError("Too many connections, refusing new client");
|
|
594
|
+
CloseDescriptor(sock);
|
|
595
|
+
}
|
|
596
|
+
} else if (errno != EAGAIN) {
|
|
597
|
+
LogError("Failed to accept client: %1", strerror(errno));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
RunClients([&](Size idx, int sock) {
|
|
602
|
+
if (!(ready & (1ull << idx)))
|
|
603
|
+
return true;
|
|
604
|
+
|
|
605
|
+
// Handle disconnection and errors first
|
|
606
|
+
{
|
|
607
|
+
struct pollfd pfd = { sock, POLLIN, 0 };
|
|
608
|
+
K_IGNORE poll(&pfd, 1, 1000);
|
|
609
|
+
|
|
610
|
+
if (pfd.revents & (POLLHUP | POLLERR)) {
|
|
611
|
+
LogDebug("Client has disconnected");
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const auto read = [&](Span<uint8_t> out_buf) {
|
|
617
|
+
Size received = recv(sock, out_buf.ptr, out_buf.len, 0);
|
|
618
|
+
if (received < 0) {
|
|
619
|
+
if (errno == EAGAIN) {
|
|
620
|
+
if (IsReadable(sock, 1000)) {
|
|
621
|
+
received = recv(sock, out_buf.ptr, out_buf.len, 0);
|
|
622
|
+
} else {
|
|
623
|
+
LogError("Client has timed out");
|
|
624
|
+
}
|
|
625
|
+
} else {
|
|
626
|
+
LogError("Failed to receive data from client: %1", strerror(errno));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return received;
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const auto write = [&](Span<const uint8_t> buf) {
|
|
634
|
+
while (buf.len) {
|
|
635
|
+
Size sent = send(sock, buf.ptr, (size_t)buf.len, 0);
|
|
636
|
+
if (sent < 0) {
|
|
637
|
+
LogError("Failed to send data to server: %1", strerror(errno));
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
buf.ptr += sent;
|
|
642
|
+
buf.len -= sent;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return true;
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
StreamReader reader(read, "<client>");
|
|
649
|
+
StreamWriter writer(write, "<client>");
|
|
650
|
+
|
|
651
|
+
if (!handle_func(&reader, &writer))
|
|
652
|
+
return false;
|
|
653
|
+
if (!reader.Close())
|
|
654
|
+
return false;
|
|
655
|
+
if (!writer.Close())
|
|
656
|
+
return false;
|
|
657
|
+
|
|
658
|
+
return true;
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
void TowerServer::Send(FunctionRef<void(StreamWriter *)> func)
|
|
665
|
+
{
|
|
666
|
+
RunClients([&](Size, int sock) {
|
|
667
|
+
const auto write = [&](Span<const uint8_t> buf) {
|
|
668
|
+
while (buf.len) {
|
|
669
|
+
Size sent = send(sock, buf.ptr, (size_t)buf.len, 0);
|
|
670
|
+
if (sent < 0) {
|
|
671
|
+
LogError("Failed to send data to server: %1", strerror(errno));
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
buf.ptr += sent;
|
|
676
|
+
buf.len -= sent;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return true;
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
StreamWriter writer(write, "<client>");
|
|
683
|
+
func(&writer);
|
|
684
|
+
|
|
685
|
+
return writer.Close();
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
void TowerServer::RunClients(FunctionRef<bool(Size, int)> func)
|
|
690
|
+
{
|
|
691
|
+
Size j = 1;
|
|
692
|
+
for (Size i = 1; i < sources.len; i++) {
|
|
693
|
+
const WaitSource &src = sources[i];
|
|
694
|
+
|
|
695
|
+
sources[j] = src;
|
|
696
|
+
|
|
697
|
+
if (!func(i, src.fd)) {
|
|
698
|
+
close(src.fd);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
j++;
|
|
703
|
+
}
|
|
704
|
+
sources.len = j;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
bool TowerClient::Connect(const char *path)
|
|
708
|
+
{
|
|
709
|
+
Stop();
|
|
710
|
+
|
|
711
|
+
K_DEFER_N(err_guard) { Stop(); };
|
|
712
|
+
|
|
713
|
+
sock = CreateSocket(SocketType::Unix, SOCK_STREAM);
|
|
714
|
+
if (sock < 0)
|
|
715
|
+
return false;
|
|
716
|
+
if (!ConnectUnixSocket(sock, path))
|
|
717
|
+
return false;
|
|
718
|
+
|
|
719
|
+
err_guard.Disable();
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
void TowerClient::Start(std::function<void(StreamReader *)> func)
|
|
724
|
+
{
|
|
725
|
+
K_ASSERT(sock >= 0);
|
|
726
|
+
K_ASSERT(!handle_func);
|
|
727
|
+
|
|
728
|
+
src = { sock, -1 };
|
|
729
|
+
handle_func = func;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
void TowerClient::Stop()
|
|
733
|
+
{
|
|
734
|
+
CloseDescriptor(sock);
|
|
735
|
+
sock = -1;
|
|
736
|
+
|
|
737
|
+
handle_func = {};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
bool TowerClient::Process()
|
|
741
|
+
{
|
|
742
|
+
// We need to poll because StreamReader does not support non-blocking reads,
|
|
743
|
+
// so make sure there's data on the other end. The caller probably knows and may
|
|
744
|
+
// have skipped the call to Process but we don't want to enforce this; Process()
|
|
745
|
+
// should work and do nothing if there's nothing to do.
|
|
746
|
+
if (!IsReadable(sock, 0))
|
|
747
|
+
return true;
|
|
748
|
+
|
|
749
|
+
const auto read = [&](Span<uint8_t> out_buf) {
|
|
750
|
+
Size received = recv(sock, out_buf.ptr, out_buf.len, 0);
|
|
751
|
+
if (received < 0) {
|
|
752
|
+
LogError("Failed to receive data from server: %1", strerror(errno));
|
|
753
|
+
}
|
|
754
|
+
return received;
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
StreamReader reader(read, "<client>");
|
|
758
|
+
handle_func(&reader);
|
|
759
|
+
|
|
760
|
+
return reader.Close();
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
bool TowerClient::Send(FunctionRef<void(StreamWriter *)> func)
|
|
764
|
+
{
|
|
765
|
+
const auto write = [&](Span<const uint8_t> buf) {
|
|
766
|
+
while (buf.len) {
|
|
767
|
+
Size sent = send(sock, buf.ptr, (size_t)buf.len, 0);
|
|
768
|
+
if (sent < 0) {
|
|
769
|
+
LogError("Failed to send data to server: %1", strerror(errno));
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
buf.ptr += sent;
|
|
774
|
+
buf.len -= sent;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
return true;
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
StreamWriter writer(write, "<server>");
|
|
781
|
+
func(&writer);
|
|
782
|
+
|
|
783
|
+
return writer.Close();
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const char *GetControlSocketPath(ControlScope scope, const char *name, Allocator *alloc)
|
|
787
|
+
{
|
|
788
|
+
K_ASSERT(strlen(name) < 64);
|
|
789
|
+
|
|
790
|
+
switch (scope) {
|
|
791
|
+
case ControlScope::System: {
|
|
792
|
+
const char *prefix = TestFile("/run", FileType::Directory) ? "/run" : "/var/run";
|
|
793
|
+
return Fmt(alloc, "%1/%2.sock", prefix, name).ptr;
|
|
794
|
+
} break;
|
|
795
|
+
|
|
796
|
+
case ControlScope::User: {
|
|
797
|
+
const char *xdg = GetEnv("XDG_RUNTIME_DIR");
|
|
798
|
+
const char *path = nullptr;
|
|
799
|
+
|
|
800
|
+
if (xdg) {
|
|
801
|
+
path = Fmt(alloc, "%1/%2.sock", xdg, name).ptr;
|
|
802
|
+
} else {
|
|
803
|
+
const char *prefix = TestFile("/run", FileType::Directory) ? "/run" : "/var/run";
|
|
804
|
+
uid_t uid = getuid();
|
|
805
|
+
|
|
806
|
+
path = Fmt(alloc, "%1/%2/%3.sock", prefix, uid, name).ptr;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Best effort
|
|
810
|
+
EnsureDirectoryExists(path);
|
|
811
|
+
|
|
812
|
+
return path;
|
|
813
|
+
} break;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
K_UNREACHABLE();
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
#endif
|
|
820
|
+
|
|
821
|
+
}
|