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.
Files changed (105) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +1 -1
  3. package/build/koffi/darwin_arm64/koffi.node +0 -0
  4. package/build/koffi/darwin_x64/koffi.node +0 -0
  5. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  6. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  7. package/build/koffi/freebsd_x64/koffi.node +0 -0
  8. package/build/koffi/linux_arm64/koffi.node +0 -0
  9. package/build/koffi/linux_armhf/koffi.node +0 -0
  10. package/build/koffi/linux_ia32/koffi.node +0 -0
  11. package/build/koffi/linux_loong64/koffi.node +0 -0
  12. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  13. package/build/koffi/linux_x64/koffi.node +0 -0
  14. package/build/koffi/musl_arm64/koffi.node +0 -0
  15. package/build/koffi/musl_x64/koffi.node +0 -0
  16. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  17. package/build/koffi/openbsd_x64/koffi.node +0 -0
  18. package/build/koffi/win32_arm64/koffi.node +0 -0
  19. package/build/koffi/win32_ia32/koffi.node +0 -0
  20. package/build/koffi/win32_x64/koffi.node +0 -0
  21. package/doc/assets.ini +2 -1
  22. package/doc/build.sh +9 -0
  23. package/doc/pages/404.md +17 -0
  24. package/doc/pages/index.md +43 -4
  25. package/doc/pages/misc.md +16 -11
  26. package/doc/pages/platforms.md +8 -19
  27. package/doc/pages.ini +4 -7
  28. package/doc/static/highlight.js +2 -14
  29. package/doc/static/koffi.css +3 -15
  30. package/doc/static/perf_windows.png +0 -0
  31. package/doc/static/print.css +2 -14
  32. package/index.d.ts +29 -24
  33. package/index.js +10 -9
  34. package/indirect.js +10 -9
  35. package/{src/core → lib/native}/base/base.cc +1753 -1089
  36. package/{src/core → lib/native}/base/base.hh +868 -572
  37. package/{src/core → lib/native}/base/crc.inc +3 -21
  38. package/lib/native/base/crc_gen.py +72 -0
  39. package/{src/core → lib/native}/base/mimetypes.inc +2 -20
  40. package/{src/core → lib/native}/base/mimetypes_gen.py +2 -21
  41. package/lib/native/base/tower.cc +821 -0
  42. package/lib/native/base/tower.hh +81 -0
  43. package/{src/core → lib/native}/base/unicode.inc +3 -21
  44. package/{src/core → lib/native}/base/unicode_gen.py +5 -42
  45. package/package.json +3 -2
  46. package/src/cnoke/assets/FindCNoke.cmake +8 -20
  47. package/src/cnoke/assets/win_delay_hook.c +2 -20
  48. package/src/cnoke/cnoke.js +2 -21
  49. package/src/cnoke/src/builder.js +3 -22
  50. package/src/cnoke/src/index.js +2 -20
  51. package/src/cnoke/src/tools.js +2 -20
  52. package/src/koffi/CMakeLists.txt +19 -22
  53. package/src/koffi/cmake/raylib.cmake +5 -22
  54. package/src/koffi/cmake/sqlite3.cmake +2 -20
  55. package/src/koffi/src/abi_arm32.cc +31 -49
  56. package/src/koffi/src/abi_arm32_asm.S +2 -20
  57. package/src/koffi/src/abi_arm64.cc +36 -54
  58. package/src/koffi/src/abi_arm64_asm.S +2 -20
  59. package/src/koffi/src/abi_arm64_asm.asm +2 -20
  60. package/src/koffi/src/abi_loong64.cc +2 -20
  61. package/src/koffi/src/abi_loong64_asm.S +2 -20
  62. package/src/koffi/src/abi_riscv64.cc +34 -52
  63. package/src/koffi/src/abi_riscv64_asm.S +2 -20
  64. package/src/koffi/src/abi_x64_sysv.cc +36 -54
  65. package/src/koffi/src/abi_x64_sysv_asm.S +2 -20
  66. package/src/koffi/src/abi_x64_win.cc +32 -50
  67. package/src/koffi/src/abi_x64_win_asm.asm +2 -20
  68. package/src/koffi/src/abi_x86.cc +33 -51
  69. package/src/koffi/src/abi_x86_asm.S +2 -20
  70. package/src/koffi/src/abi_x86_asm.asm +2 -20
  71. package/src/koffi/src/call.cc +107 -281
  72. package/src/koffi/src/call.hh +9 -27
  73. package/src/koffi/src/errno.inc +2 -20
  74. package/src/koffi/src/ffi.cc +121 -121
  75. package/src/koffi/src/ffi.hh +23 -38
  76. package/src/koffi/src/init.js +2 -20
  77. package/src/koffi/src/parser.cc +15 -29
  78. package/src/koffi/src/parser.hh +4 -22
  79. package/src/koffi/src/trampolines/armasm.inc +0 -21
  80. package/src/koffi/src/trampolines/gnu.inc +0 -21
  81. package/src/koffi/src/trampolines/masm32.inc +0 -21
  82. package/src/koffi/src/trampolines/masm64.inc +0 -21
  83. package/src/koffi/src/trampolines/prototypes.inc +1 -22
  84. package/src/koffi/src/util.cc +87 -102
  85. package/src/koffi/src/util.hh +11 -29
  86. package/src/koffi/src/uv.cc +193 -0
  87. package/src/koffi/src/uv.def +10 -0
  88. package/src/koffi/src/uv.hh +40 -0
  89. package/src/koffi/src/win32.cc +7 -25
  90. package/src/koffi/src/win32.hh +4 -22
  91. package/vendor/node-api-headers/include/uv/aix.h +32 -0
  92. package/vendor/node-api-headers/include/uv/bsd.h +34 -0
  93. package/vendor/node-api-headers/include/uv/darwin.h +61 -0
  94. package/vendor/node-api-headers/include/uv/errno.h +483 -0
  95. package/vendor/node-api-headers/include/uv/linux.h +34 -0
  96. package/vendor/node-api-headers/include/uv/os390.h +33 -0
  97. package/vendor/node-api-headers/include/uv/posix.h +31 -0
  98. package/vendor/node-api-headers/include/uv/sunos.h +44 -0
  99. package/vendor/node-api-headers/include/uv/threadpool.h +37 -0
  100. package/vendor/node-api-headers/include/uv/tree.h +521 -0
  101. package/vendor/node-api-headers/include/uv/unix.h +512 -0
  102. package/vendor/node-api-headers/include/uv/version.h +43 -0
  103. package/vendor/node-api-headers/include/uv/win.h +698 -0
  104. package/vendor/node-api-headers/include/uv.h +1990 -0
  105. 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
+ }