simple-ipc: add win32 implementation
Create Windows implementation of "simple-ipc" using named pipes. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff Hostetler committed
Mar 15, 2021 at 21:08 UTC
59c7b88198af8af29deffb2eee7b7b2329e9197e
6 files changed
+1018
Makefile
+5
@@ -1679,6 +1679,11 @@ else
1679
LIB_OBJS += unix-socket.o
1680
endif
1681
1682
+ifdef USE_WIN32_IPC
1683
+ LIB_OBJS += compat/simple-ipc/ipc-shared.o
1684
+ LIB_OBJS += compat/simple-ipc/ipc-win32.o
1685
+endif
1686
+
1687
ifdef NO_ICONV
1688
BASIC_CFLAGS += -DNO_ICONV
1689
endif
compat/simple-ipc/ipc-shared.c
new
+28
@@ -0,0 +1,28 @@
1
+#include "cache.h"
2
+#include "simple-ipc.h"
3
+#include "strbuf.h"
4
+#include "pkt-line.h"
5
+#include "thread-utils.h"
6
+
7
+#ifdef SUPPORTS_SIMPLE_IPC
8
+
9
+int ipc_server_run(const char *path, const struct ipc_server_opts *opts,
10
+ ipc_server_application_cb *application_cb,
11
+ void *application_data)
12
+{
13
+ struct ipc_server_data *server_data = NULL;
14
+ int ret;
15
+
16
+ ret = ipc_server_run_async(&server_data, path, opts,
17
+ application_cb, application_data);
18
+ if (ret)
19
+ return ret;
20
+
21
+ ret = ipc_server_await(server_data);
22
+
23
+ ipc_server_free(server_data);
24
+
25
+ return ret;
26
+}
27
+
28
+#endif /* SUPPORTS_SIMPLE_IPC */
compat/simple-ipc/ipc-win32.c
new
+751
@@ -0,0 +1,751 @@
1
+#include "cache.h"
2
+#include "simple-ipc.h"
3
+#include "strbuf.h"
4
+#include "pkt-line.h"
5
+#include "thread-utils.h"
6
+
7
+#ifndef GIT_WINDOWS_NATIVE
8
+#error This file can only be compiled on Windows
9
+#endif
10
+
11
+static int initialize_pipe_name(const char *path, wchar_t *wpath, size_t alloc)
12
+{
13
+ int off = 0;
14
+ struct strbuf realpath = STRBUF_INIT;
15
+
16
+ if (!strbuf_realpath(&realpath, path, 0))
17
+ return -1;
18
+
19
+ off = swprintf(wpath, alloc, L"\\\\.\\pipe\\");
20
+ if (xutftowcs(wpath + off, realpath.buf, alloc - off) < 0)
21
+ return -1;
22
+
23
+ /* Handle drive prefix */
24
+ if (wpath[off] && wpath[off + 1] == L':') {
25
+ wpath[off + 1] = L'_';
26
+ off += 2;
27
+ }
28
+
29
+ for (; wpath[off]; off++)
30
+ if (wpath[off] == L'/')
31
+ wpath[off] = L'\\';
32
+
33
+ strbuf_release(&realpath);
34
+ return 0;
35
+}
36
+
37
+static enum ipc_active_state get_active_state(wchar_t *pipe_path)
38
+{
39
+ if (WaitNamedPipeW(pipe_path, NMPWAIT_USE_DEFAULT_WAIT))
40
+ return IPC_STATE__LISTENING;
41
+
42
+ if (GetLastError() == ERROR_SEM_TIMEOUT)
43
+ return IPC_STATE__NOT_LISTENING;
44
+
45
+ if (GetLastError() == ERROR_FILE_NOT_FOUND)
46
+ return IPC_STATE__PATH_NOT_FOUND;
47
+
48
+ return IPC_STATE__OTHER_ERROR;
49
+}
50
+
51
+enum ipc_active_state ipc_get_active_state(const char *path)
52
+{
53
+ wchar_t pipe_path[MAX_PATH];
54
+
55
+ if (initialize_pipe_name(path, pipe_path, ARRAY_SIZE(pipe_path)) < 0)
56
+ return IPC_STATE__INVALID_PATH;
57
+
58
+ return get_active_state(pipe_path);
59
+}
60
+
61
+#define WAIT_STEP_MS (50)
62
+
63
+static enum ipc_active_state connect_to_server(
64
+ const wchar_t *wpath,
65
+ DWORD timeout_ms,
66
+ const struct ipc_client_connect_options *options,
67
+ int *pfd)
68
+{
69
+ DWORD t_start_ms, t_waited_ms;
70
+ DWORD step_ms;
71
+ HANDLE hPipe = INVALID_HANDLE_VALUE;
72
+ DWORD mode = PIPE_READMODE_BYTE;
73
+ DWORD gle;
74
+
75
+ *pfd = -1;
76
+
77
+ for (;;) {
78
+ hPipe = CreateFileW(wpath, GENERIC_READ | GENERIC_WRITE,
79
+ 0, NULL, OPEN_EXISTING, 0, NULL);
80
+ if (hPipe != INVALID_HANDLE_VALUE)
81
+ break;
82
+
83
+ gle = GetLastError();
84
+
85
+ switch (gle) {
86
+ case ERROR_FILE_NOT_FOUND:
87
+ if (!options->wait_if_not_found)
88
+ return IPC_STATE__PATH_NOT_FOUND;
89
+ if (!timeout_ms)
90
+ return IPC_STATE__PATH_NOT_FOUND;
91
+
92
+ step_ms = (timeout_ms < WAIT_STEP_MS) ?
93
+ timeout_ms : WAIT_STEP_MS;
94
+ sleep_millisec(step_ms);
95
+
96
+ timeout_ms -= step_ms;
97
+ break; /* try again */
98
+
99
+ case ERROR_PIPE_BUSY:
100
+ if (!options->wait_if_busy)
101
+ return IPC_STATE__NOT_LISTENING;
102
+ if (!timeout_ms)
103
+ return IPC_STATE__NOT_LISTENING;
104
+
105
+ t_start_ms = (DWORD)(getnanotime() / 1000000);
106
+
107
+ if (!WaitNamedPipeW(wpath, timeout_ms)) {
108
+ if (GetLastError() == ERROR_SEM_TIMEOUT)
109
+ return IPC_STATE__NOT_LISTENING;
110
+
111
+ return IPC_STATE__OTHER_ERROR;
112
+ }
113
+
114
+ /*
115
+ * A pipe server instance became available.
116
+ * Race other client processes to connect to
117
+ * it.
118
+ *
119
+ * But first decrement our overall timeout so
120
+ * that we don't starve if we keep losing the
121
+ * race. But also guard against special
122
+ * NPMWAIT_ values (0 and -1).
123
+ */
124
+ t_waited_ms = (DWORD)(getnanotime() / 1000000) - t_start_ms;
125
+ if (t_waited_ms < timeout_ms)
126
+ timeout_ms -= t_waited_ms;
127
+ else
128
+ timeout_ms = 1;
129
+ break; /* try again */
130
+
131
+ default:
132
+ return IPC_STATE__OTHER_ERROR;
133
+ }
134
+ }
135
+
136
+ if (!SetNamedPipeHandleState(hPipe, &mode, NULL, NULL)) {
137
+ CloseHandle(hPipe);
138
+ return IPC_STATE__OTHER_ERROR;
139
+ }
140
+
141
+ *pfd = _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY);
142
+ if (*pfd < 0) {
143
+ CloseHandle(hPipe);
144
+ return IPC_STATE__OTHER_ERROR;
145
+ }
146
+
147
+ /* fd now owns hPipe */
148
+
149
+ return IPC_STATE__LISTENING;
150
+}
151
+
152
+/*
153
+ * The default connection timeout for Windows clients.
154
+ *
155
+ * This is not currently part of the ipc_ API (nor the config settings)
156
+ * because of differences between Windows and other platforms.
157
+ *
158
+ * This value was chosen at random.
159
+ */
160
+#define WINDOWS_CONNECTION_TIMEOUT_MS (30000)
161
+
162
+enum ipc_active_state ipc_client_try_connect(
163
+ const char *path,
164
+ const struct ipc_client_connect_options *options,
165
+ struct ipc_client_connection **p_connection)
166
+{
167
+ wchar_t wpath[MAX_PATH];
168
+ enum ipc_active_state state = IPC_STATE__OTHER_ERROR;
169
+ int fd = -1;
170
+
171
+ *p_connection = NULL;
172
+
173
+ trace2_region_enter("ipc-client", "try-connect", NULL);
174
+ trace2_data_string("ipc-client", NULL, "try-connect/path", path);
175
+
176
+ if (initialize_pipe_name(path, wpath, ARRAY_SIZE(wpath)) < 0)
177
+ state = IPC_STATE__INVALID_PATH;
178
+ else
179
+ state = connect_to_server(wpath, WINDOWS_CONNECTION_TIMEOUT_MS,
180
+ options, &fd);
181
+
182
+ trace2_data_intmax("ipc-client", NULL, "try-connect/state",
183
+ (intmax_t)state);
184
+ trace2_region_leave("ipc-client", "try-connect", NULL);
185
+
186
+ if (state == IPC_STATE__LISTENING) {
187
+ (*p_connection) = xcalloc(1, sizeof(struct ipc_client_connection));
188
+ (*p_connection)->fd = fd;
189
+ }
190
+
191
+ return state;
192
+}
193
+
194
+void ipc_client_close_connection(struct ipc_client_connection *connection)
195
+{
196
+ if (!connection)
197
+ return;
198
+
199
+ if (connection->fd != -1)
200
+ close(connection->fd);
201
+
202
+ free(connection);
203
+}
204
+
205
+int ipc_client_send_command_to_connection(
206
+ struct ipc_client_connection *connection,
207
+ const char *message, struct strbuf *answer)
208
+{
209
+ int ret = 0;
210
+
211
+ strbuf_setlen(answer, 0);
212
+
213
+ trace2_region_enter("ipc-client", "send-command", NULL);
214
+
215
+ if (write_packetized_from_buf_no_flush(message, strlen(message),
216
+ connection->fd) < 0 ||
217
+ packet_flush_gently(connection->fd) < 0) {
218
+ ret = error(_("could not send IPC command"));
219
+ goto done;
220
+ }
221
+
222
+ FlushFileBuffers((HANDLE)_get_osfhandle(connection->fd));
223
+
224
+ if (read_packetized_to_strbuf(
225
+ connection->fd, answer,
226
+ PACKET_READ_GENTLE_ON_EOF | PACKET_READ_GENTLE_ON_READ_ERROR) < 0) {
227
+ ret = error(_("could not read IPC response"));
228
+ goto done;
229
+ }
230
+
231
+done:
232
+ trace2_region_leave("ipc-client", "send-command", NULL);
233
+ return ret;
234
+}
235
+
236
+int ipc_client_send_command(const char *path,
237
+ const struct ipc_client_connect_options *options,
238
+ const char *message, struct strbuf *response)
239
+{
240
+ int ret = -1;
241
+ enum ipc_active_state state;
242
+ struct ipc_client_connection *connection = NULL;
243
+
244
+ state = ipc_client_try_connect(path, options, &connection);
245
+
246
+ if (state != IPC_STATE__LISTENING)
247
+ return ret;
248
+
249
+ ret = ipc_client_send_command_to_connection(connection, message, response);
250
+
251
+ ipc_client_close_connection(connection);
252
+
253
+ return ret;
254
+}
255
+
256
+/*
257
+ * Duplicate the given pipe handle and wrap it in a file descriptor so
258
+ * that we can use pkt-line on it.
259
+ */
260
+static int dup_fd_from_pipe(const HANDLE pipe)
261
+{
262
+ HANDLE process = GetCurrentProcess();
263
+ HANDLE handle;
264
+ int fd;
265
+
266
+ if (!DuplicateHandle(process, pipe, process, &handle, 0, FALSE,
267
+ DUPLICATE_SAME_ACCESS)) {
268
+ errno = err_win_to_posix(GetLastError());
269
+ return -1;
270
+ }
271
+
272
+ fd = _open_osfhandle((intptr_t)handle, O_RDWR|O_BINARY);
273
+ if (fd < 0) {
274
+ errno = err_win_to_posix(GetLastError());
275
+ CloseHandle(handle);
276
+ return -1;
277
+ }
278
+
279
+ /*
280
+ * `handle` is now owned by `fd` and will be automatically closed
281
+ * when the descriptor is closed.
282
+ */
283
+
284
+ return fd;
285
+}
286
+
287
+/*
288
+ * Magic numbers used to annotate callback instance data.
289
+ * These are used to help guard against accidentally passing the
290
+ * wrong instance data across multiple levels of callbacks (which
291
+ * is easy to do if there are `void*` arguments).
292
+ */
293
+enum magic {
294
+ MAGIC_SERVER_REPLY_DATA,
295
+ MAGIC_SERVER_THREAD_DATA,
296
+ MAGIC_SERVER_DATA,
297
+};
298
+
299
+struct ipc_server_reply_data {
300
+ enum magic magic;
301
+ int fd;
302
+ struct ipc_server_thread_data *server_thread_data;
303
+};
304
+
305
+struct ipc_server_thread_data {
306
+ enum magic magic;
307
+ struct ipc_server_thread_data *next_thread;
308
+ struct ipc_server_data *server_data;
309
+ pthread_t pthread_id;
310
+ HANDLE hPipe;
311
+};
312
+
313
+/*
314
+ * On Windows, the conceptual "ipc-server" is implemented as a pool of
315
+ * n idential/peer "server-thread" threads. That is, there is no
316
+ * hierarchy of threads; and therefore no controller thread managing
317
+ * the pool. Each thread has an independent handle to the named pipe,
318
+ * receives incoming connections, processes the client, and re-uses
319
+ * the pipe for the next client connection.
320
+ *
321
+ * Therefore, the "ipc-server" only needs to maintain a list of the
322
+ * spawned threads for eventual "join" purposes.
323
+ *
324
+ * A single "stop-event" is visible to all of the server threads to
325
+ * tell them to shutdown (when idle).
326
+ */
327
+struct ipc_server_data {
328
+ enum magic magic;
329
+ ipc_server_application_cb *application_cb;
330
+ void *application_data;
331
+ struct strbuf buf_path;
332
+ wchar_t wpath[MAX_PATH];
333
+
334
+ HANDLE hEventStopRequested;
335
+ struct ipc_server_thread_data *thread_list;
336
+ int is_stopped;
337
+};
338
+
339
+enum connect_result {
340
+ CR_CONNECTED = 0,
341
+ CR_CONNECT_PENDING,
342
+ CR_CONNECT_ERROR,
343
+ CR_WAIT_ERROR,
344
+ CR_SHUTDOWN,
345
+};
346
+
347
+static enum connect_result queue_overlapped_connect(
348
+ struct ipc_server_thread_data *server_thread_data,
349
+ OVERLAPPED *lpo)
350
+{
351
+ if (ConnectNamedPipe(server_thread_data->hPipe, lpo))
352
+ goto failed;
353
+
354
+ switch (GetLastError()) {
355
+ case ERROR_IO_PENDING:
356
+ return CR_CONNECT_PENDING;
357
+
358
+ case ERROR_PIPE_CONNECTED:
359
+ SetEvent(lpo->hEvent);
360
+ return CR_CONNECTED;
361
+
362
+ default:
363
+ break;
364
+ }
365
+
366
+failed:
367
+ error(_("ConnectNamedPipe failed for '%s' (%lu)"),
368
+ server_thread_data->server_data->buf_path.buf,
369
+ GetLastError());
370
+ return CR_CONNECT_ERROR;
371
+}
372
+
373
+/*
374
+ * Use Windows Overlapped IO to wait for a connection or for our event
375
+ * to be signalled.
376
+ */
377
+static enum connect_result wait_for_connection(
378
+ struct ipc_server_thread_data *server_thread_data,
379
+ OVERLAPPED *lpo)
380
+{
381
+ enum connect_result r;
382
+ HANDLE waitHandles[2];
383
+ DWORD dwWaitResult;
384
+
385
+ r = queue_overlapped_connect(server_thread_data, lpo);
386
+ if (r != CR_CONNECT_PENDING)
387
+ return r;
388
+
389
+ waitHandles[0] = server_thread_data->server_data->hEventStopRequested;
390
+ waitHandles[1] = lpo->hEvent;
391
+
392
+ dwWaitResult = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
393
+ switch (dwWaitResult) {
394
+ case WAIT_OBJECT_0 + 0:
395
+ return CR_SHUTDOWN;
396
+
397
+ case WAIT_OBJECT_0 + 1:
398
+ ResetEvent(lpo->hEvent);
399
+ return CR_CONNECTED;
400
+
401
+ default:
402
+ return CR_WAIT_ERROR;
403
+ }
404
+}
405
+
406
+/*
407
+ * Forward declare our reply callback function so that any compiler
408
+ * errors are reported when we actually define the function (in addition
409
+ * to any errors reported when we try to pass this callback function as
410
+ * a parameter in a function call). The former are easier to understand.
411
+ */
412
+static ipc_server_reply_cb do_io_reply_callback;
413
+
414
+/*
415
+ * Relay application's response message to the client process.
416
+ * (We do not flush at this point because we allow the caller
417
+ * to chunk data to the client thru us.)
418
+ */
419
+static int do_io_reply_callback(struct ipc_server_reply_data *reply_data,
420
+ const char *response, size_t response_len)
421
+{
422
+ if (reply_data->magic != MAGIC_SERVER_REPLY_DATA)
423
+ BUG("reply_cb called with wrong instance data");
424
+
425
+ return write_packetized_from_buf_no_flush(response, response_len,
426
+ reply_data->fd);
427
+}
428
+
429
+/*
430
+ * Receive the request/command from the client and pass it to the
431
+ * registered request-callback. The request-callback will compose
432
+ * a response and call our reply-callback to send it to the client.
433
+ *
434
+ * Simple-IPC only contains one round trip, so we flush and close
435
+ * here after the response.
436
+ */
437
+static int do_io(struct ipc_server_thread_data *server_thread_data)
438
+{
439
+ struct strbuf buf = STRBUF_INIT;
440
+ struct ipc_server_reply_data reply_data;
441
+ int ret = 0;
442
+
443
+ reply_data.magic = MAGIC_SERVER_REPLY_DATA;
444
+ reply_data.server_thread_data = server_thread_data;
445
+
446
+ reply_data.fd = dup_fd_from_pipe(server_thread_data->hPipe);
447
+ if (reply_data.fd < 0)
448
+ return error(_("could not create fd from pipe for '%s'"),
449
+ server_thread_data->server_data->buf_path.buf);
450
+
451
+ ret = read_packetized_to_strbuf(
452
+ reply_data.fd, &buf,
453
+ PACKET_READ_GENTLE_ON_EOF | PACKET_READ_GENTLE_ON_READ_ERROR);
454
+ if (ret >= 0) {
455
+ ret = server_thread_data->server_data->application_cb(
456
+ server_thread_data->server_data->application_data,
457
+ buf.buf, do_io_reply_callback, &reply_data);
458
+
459
+ packet_flush_gently(reply_data.fd);
460
+
461
+ FlushFileBuffers((HANDLE)_get_osfhandle((reply_data.fd)));
462
+ }
463
+ else {
464
+ /*
465
+ * The client probably disconnected/shutdown before it
466
+ * could send a well-formed message. Ignore it.
467
+ */
468
+ }
469
+
470
+ strbuf_release(&buf);
471
+ close(reply_data.fd);
472
+
473
+ return ret;
474
+}
475
+
476
+/*
477
+ * Handle IPC request and response with this connected client. And reset
478
+ * the pipe to prepare for the next client.
479
+ */
480
+static int use_connection(struct ipc_server_thread_data *server_thread_data)
481
+{
482
+ int ret;
483
+
484
+ ret = do_io(server_thread_data);
485
+
486
+ FlushFileBuffers(server_thread_data->hPipe);
487
+ DisconnectNamedPipe(server_thread_data->hPipe);
488
+
489
+ return ret;
490
+}
491
+
492
+/*
493
+ * Thread proc for an IPC server worker thread. It handles a series of
494
+ * connections from clients. It cleans and reuses the hPipe between each
495
+ * client.
496
+ */
497
+static void *server_thread_proc(void *_server_thread_data)
498
+{
499
+ struct ipc_server_thread_data *server_thread_data = _server_thread_data;
500
+ HANDLE hEventConnected = INVALID_HANDLE_VALUE;
501
+ OVERLAPPED oConnect;
502
+ enum connect_result cr;
503
+ int ret;
504
+
505
+ assert(server_thread_data->hPipe != INVALID_HANDLE_VALUE);
506
+
507
+ trace2_thread_start("ipc-server");
508
+ trace2_data_string("ipc-server", NULL, "pipe",
509
+ server_thread_data->server_data->buf_path.buf);
510
+
511
+ hEventConnected = CreateEventW(NULL, TRUE, FALSE, NULL);
512
+
513
+ memset(&oConnect, 0, sizeof(oConnect));
514
+ oConnect.hEvent = hEventConnected;
515
+
516
+ for (;;) {
517
+ cr = wait_for_connection(server_thread_data, &oConnect);
518
+
519
+ switch (cr) {
520
+ case CR_SHUTDOWN:
521
+ goto finished;
522
+
523
+ case CR_CONNECTED:
524
+ ret = use_connection(server_thread_data);
525
+ if (ret == SIMPLE_IPC_QUIT) {
526
+ ipc_server_stop_async(
527
+ server_thread_data->server_data);
528
+ goto finished;
529
+ }
530
+ if (ret > 0) {
531
+ /*
532
+ * Ignore (transient) IO errors with this
533
+ * client and reset for the next client.
534
+ */
535
+ }
536
+ break;
537
+
538
+ case CR_CONNECT_PENDING:
539
+ /* By construction, this should not happen. */
540
+ BUG("ipc-server[%s]: unexpeced CR_CONNECT_PENDING",
541
+ server_thread_data->server_data->buf_path.buf);
542
+
543
+ case CR_CONNECT_ERROR:
544
+ case CR_WAIT_ERROR:
545
+ /*
546
+ * Ignore these theoretical errors.
547
+ */
548
+ DisconnectNamedPipe(server_thread_data->hPipe);
549
+ break;
550
+
551
+ default:
552
+ BUG("unandled case after wait_for_connection");
553
+ }
554
+ }
555
+
556
+finished:
557
+ CloseHandle(server_thread_data->hPipe);
558
+ CloseHandle(hEventConnected);
559
+
560
+ trace2_thread_exit();
561
+ return NULL;
562
+}
563
+
564
+static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
565
+{
566
+ HANDLE hPipe;
567
+ DWORD dwOpenMode, dwPipeMode;
568
+ LPSECURITY_ATTRIBUTES lpsa = NULL;
569
+
570
+ dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
571
+ FILE_FLAG_OVERLAPPED;
572
+
573
+ dwPipeMode = PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT |
574
+ PIPE_REJECT_REMOTE_CLIENTS;
575
+
576
+ if (is_first) {
577
+ dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
578
+
579
+ /*
580
+ * On Windows, the first server pipe instance gets to
581
+ * set the ACL / Security Attributes on the named
582
+ * pipe; subsequent instances inherit and cannot
583
+ * change them.
584
+ *
585
+ * TODO Should we allow the application layer to
586
+ * specify security attributes, such as `LocalService`
587
+ * or `LocalSystem`, when we create the named pipe?
588
+ * This question is probably not important when the
589
+ * daemon is started by a foreground user process and
590
+ * only needs to talk to the current user, but may be
591
+ * if the daemon is run via the Control Panel as a
592
+ * System Service.
593
+ */
594
+ }
595
+
596
+ hPipe = CreateNamedPipeW(wpath, dwOpenMode, dwPipeMode,
597
+ PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, lpsa);
598
+
599
+ return hPipe;
600
+}
601
+
602
+int ipc_server_run_async(struct ipc_server_data **returned_server_data,
603
+ const char *path, const struct ipc_server_opts *opts,
604
+ ipc_server_application_cb *application_cb,
605
+ void *application_data)
606
+{
607
+ struct ipc_server_data *server_data;
608
+ wchar_t wpath[MAX_PATH];
609
+ HANDLE hPipeFirst = INVALID_HANDLE_VALUE;
610
+ int k;
611
+ int ret = 0;
612
+ int nr_threads = opts->nr_threads;
613
+
614
+ *returned_server_data = NULL;
615
+
616
+ ret = initialize_pipe_name(path, wpath, ARRAY_SIZE(wpath));
617
+ if (ret < 0) {
618
+ errno = EINVAL;
619
+ return -1;
620
+ }
621
+
622
+ hPipeFirst = create_new_pipe(wpath, 1);
623
+ if (hPipeFirst == INVALID_HANDLE_VALUE) {
624
+ errno = EADDRINUSE;
625
+ return -2;
626
+ }
627
+
628
+ server_data = xcalloc(1, sizeof(*server_data));
629
+ server_data->magic = MAGIC_SERVER_DATA;
630
+ server_data->application_cb = application_cb;
631
+ server_data->application_data = application_data;
632
+ server_data->hEventStopRequested = CreateEvent(NULL, TRUE, FALSE, NULL);
633
+ strbuf_init(&server_data->buf_path, 0);
634
+ strbuf_addstr(&server_data->buf_path, path);
635
+ wcscpy(server_data->wpath, wpath);
636
+
637
+ if (nr_threads < 1)
638
+ nr_threads = 1;
639
+
640
+ for (k = 0; k < nr_threads; k++) {
641
+ struct ipc_server_thread_data *std;
642
+
643
+ std = xcalloc(1, sizeof(*std));
644
+ std->magic = MAGIC_SERVER_THREAD_DATA;
645
+ std->server_data = server_data;
646
+ std->hPipe = INVALID_HANDLE_VALUE;
647
+
648
+ std->hPipe = (k == 0)
649
+ ? hPipeFirst
650
+ : create_new_pipe(server_data->wpath, 0);
651
+
652
+ if (std->hPipe == INVALID_HANDLE_VALUE) {
653
+ /*
654
+ * If we've reached a pipe instance limit for
655
+ * this path, just use fewer threads.
656
+ */
657
+ free(std);
658
+ break;
659
+ }
660
+
661
+ if (pthread_create(&std->pthread_id, NULL,
662
+ server_thread_proc, std)) {
663
+ /*
664
+ * Likewise, if we're out of threads, just use
665
+ * fewer threads than requested.
666
+ *
667
+ * However, we just give up if we can't even get
668
+ * one thread. This should not happen.
669
+ */
670
+ if (k == 0)
671
+ die(_("could not start thread[0] for '%s'"),
672
+ path);
673
+
674
+ CloseHandle(std->hPipe);
675
+ free(std);
676
+ break;
677
+ }
678
+
679
+ std->next_thread = server_data->thread_list;
680
+ server_data->thread_list = std;
681
+ }
682
+
683
+ *returned_server_data = server_data;
684
+ return 0;
685
+}
686
+
687
+int ipc_server_stop_async(struct ipc_server_data *server_data)
688
+{
689
+ if (!server_data)
690
+ return 0;
691
+
692
+ /*
693
+ * Gently tell all of the ipc_server threads to shutdown.
694
+ * This will be seen the next time they are idle (and waiting
695
+ * for a connection).
696
+ *
697
+ * We DO NOT attempt to force them to drop an active connection.
698
+ */
699
+ SetEvent(server_data->hEventStopRequested);
700
+ return 0;
701
+}
702
+
703
+int ipc_server_await(struct ipc_server_data *server_data)
704
+{
705
+ DWORD dwWaitResult;
706
+
707
+ if (!server_data)
708
+ return 0;
709
+
710
+ dwWaitResult = WaitForSingleObject(server_data->hEventStopRequested, INFINITE);
711
+ if (dwWaitResult != WAIT_OBJECT_0)
712
+ return error(_("wait for hEvent failed for '%s'"),
713
+ server_data->buf_path.buf);
714
+
715
+ while (server_data->thread_list) {
716
+ struct ipc_server_thread_data *std = server_data->thread_list;
717
+
718
+ pthread_join(std->pthread_id, NULL);
719
+
720
+ server_data->thread_list = std->next_thread;
721
+ free(std);
722
+ }
723
+
724
+ server_data->is_stopped = 1;
725
+
726
+ return 0;
727
+}
728
+
729
+void ipc_server_free(struct ipc_server_data *server_data)
730
+{
731
+ if (!server_data)
732
+ return;
733
+
734
+ if (!server_data->is_stopped)
735
+ BUG("cannot free ipc-server while running for '%s'",
736
+ server_data->buf_path.buf);
737
+
738
+ strbuf_release(&server_data->buf_path);
739
+
740
+ if (server_data->hEventStopRequested != INVALID_HANDLE_VALUE)
741
+ CloseHandle(server_data->hEventStopRequested);
742
+
743
+ while (server_data->thread_list) {
744
+ struct ipc_server_thread_data *std = server_data->thread_list;
745
+
746
+ server_data->thread_list = std->next_thread;
747
+ free(std);
748
+ }
749
+
750
+ free(server_data);
751
+}
config.mak.uname
+2
@@ -421,6 +421,7 @@ ifeq ($(uname_S),Windows)
421
RUNTIME_PREFIX = YesPlease
422
HAVE_WPGMPTR = YesWeDo
423
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
424
+ USE_WIN32_IPC = YesPlease
425
USE_WIN32_MMAP = YesPlease
426
MMAP_PREVENTS_DELETE = UnfortunatelyYes
427
# USE_NED_ALLOCATOR = YesPlease
@@ -597,6 +598,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
598
RUNTIME_PREFIX = YesPlease
599
HAVE_WPGMPTR = YesWeDo
600
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
601
+ USE_WIN32_IPC = YesPlease
602
USE_WIN32_MMAP = YesPlease
603
MMAP_PREVENTS_DELETE = UnfortunatelyYes
604
USE_NED_ALLOCATOR = YesPlease
contrib/buildsystems/CMakeLists.txt
+4
@@ -246,6 +246,10 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
246
list(APPEND compat_SOURCES unix-socket.c)
247
endif()
248
249
+if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
250
+ list(APPEND compat_SOURCES compat/simple-ipc/ipc-shared.c compat/simple-ipc/ipc-win32.c)
251
+endif()
252
+
253
set(EXE_EXTENSION ${CMAKE_EXECUTABLE_SUFFIX})
254
255
#header checks
simple-ipc.h
new
+228
@@ -0,0 +1,228 @@
1
+#ifndef GIT_SIMPLE_IPC_H
2
+#define GIT_SIMPLE_IPC_H
3
+
4
+/*
5
+ * See Documentation/technical/api-simple-ipc.txt
6
+ */
7
+
8
+#if defined(GIT_WINDOWS_NATIVE)
9
+#define SUPPORTS_SIMPLE_IPC
10
+#endif
11
+
12
+#ifdef SUPPORTS_SIMPLE_IPC
13
+#include "pkt-line.h"
14
+
15
+/*
16
+ * Simple IPC Client Side API.
17
+ */
18
+
19
+enum ipc_active_state {
20
+ /*
21
+ * The pipe/socket exists and the daemon is waiting for connections.
22
+ */
23
+ IPC_STATE__LISTENING = 0,
24
+
25
+ /*
26
+ * The pipe/socket exists, but the daemon is not listening.
27
+ * Perhaps it is very busy.
28
+ * Perhaps the daemon died without deleting the path.
29
+ * Perhaps it is shutting down and draining existing clients.
30
+ * Perhaps it is dead, but other clients are lingering and
31
+ * still holding a reference to the pathname.
32
+ */
33
+ IPC_STATE__NOT_LISTENING,
34
+
35
+ /*
36
+ * The requested pathname is bogus and no amount of retries
37
+ * will fix that.
38
+ */
39
+ IPC_STATE__INVALID_PATH,
40
+
41
+ /*
42
+ * The requested pathname is not found. This usually means
43
+ * that there is no daemon present.
44
+ */
45
+ IPC_STATE__PATH_NOT_FOUND,
46
+
47
+ IPC_STATE__OTHER_ERROR,
48
+};
49
+
50
+struct ipc_client_connect_options {
51
+ /*
52
+ * Spin under timeout if the server is running but can't
53
+ * accept our connection yet. This should always be set
54
+ * unless you just want to poke the server and see if it
55
+ * is alive.
56
+ */
57
+ unsigned int wait_if_busy:1;
58
+
59
+ /*
60
+ * Spin under timeout if the pipe/socket is not yet present
61
+ * on the file system. This is useful if we just started
62
+ * the service and need to wait for it to become ready.
63
+ */
64
+ unsigned int wait_if_not_found:1;
65
+};
66
+
67
+#define IPC_CLIENT_CONNECT_OPTIONS_INIT { \
68
+ .wait_if_busy = 0, \
69
+ .wait_if_not_found = 0, \
70
+}
71
+
72
+/*
73
+ * Determine if a server is listening on this named pipe or socket using
74
+ * platform-specific logic. This might just probe the filesystem or it
75
+ * might make a trivial connection to the server using this pathname.
76
+ */
77
+enum ipc_active_state ipc_get_active_state(const char *path);
78
+
79
+struct ipc_client_connection {
80
+ int fd;
81
+};
82
+
83
+/*
84
+ * Try to connect to the daemon on the named pipe or socket.
85
+ *
86
+ * Returns IPC_STATE__LISTENING and a connection handle.
87
+ *
88
+ * Otherwise, returns info to help decide whether to retry or to
89
+ * spawn/respawn the server.
90
+ */
91
+enum ipc_active_state ipc_client_try_connect(
92
+ const char *path,
93
+ const struct ipc_client_connect_options *options,
94
+ struct ipc_client_connection **p_connection);
95
+
96
+void ipc_client_close_connection(struct ipc_client_connection *connection);
97
+
98
+/*
99
+ * Used by the client to synchronously send and receive a message with
100
+ * the server on the provided client connection.
101
+ *
102
+ * Returns 0 when successful.
103
+ *
104
+ * Calls error() and returns non-zero otherwise.
105
+ */
106
+int ipc_client_send_command_to_connection(
107
+ struct ipc_client_connection *connection,
108
+ const char *message, struct strbuf *answer);
109
+
110
+/*
111
+ * Used by the client to synchronously connect and send and receive a
112
+ * message to the server listening at the given path.
113
+ *
114
+ * Returns 0 when successful.
115
+ *
116
+ * Calls error() and returns non-zero otherwise.
117
+ */
118
+int ipc_client_send_command(const char *path,
119
+ const struct ipc_client_connect_options *options,
120
+ const char *message, struct strbuf *answer);
121
+
122
+/*
123
+ * Simple IPC Server Side API.
124
+ */
125
+
126
+struct ipc_server_reply_data;
127
+
128
+typedef int (ipc_server_reply_cb)(struct ipc_server_reply_data *,
129
+ const char *response,
130
+ size_t response_len);
131
+
132
+/*
133
+ * Prototype for an application-supplied callback to process incoming
134
+ * client IPC messages and compose a reply. The `application_cb` should
135
+ * use the provided `reply_cb` and `reply_data` to send an IPC response
136
+ * back to the client. The `reply_cb` callback can be called multiple
137
+ * times for chunking purposes. A reply message is optional and may be
138
+ * omitted if not necessary for the application.
139
+ *
140
+ * The return value from the application callback is ignored.
141
+ * The value `SIMPLE_IPC_QUIT` can be used to shutdown the server.
142
+ */
143
+typedef int (ipc_server_application_cb)(void *application_data,
144
+ const char *request,
145
+ ipc_server_reply_cb *reply_cb,
146
+ struct ipc_server_reply_data *reply_data);
147
+
148
+#define SIMPLE_IPC_QUIT -2
149
+
150
+/*
151
+ * Opaque instance data to represent an IPC server instance.
152
+ */
153
+struct ipc_server_data;
154
+
155
+/*
156
+ * Control parameters for the IPC server instance.
157
+ * Use this to hide platform-specific settings.
158
+ */
159
+struct ipc_server_opts
160
+{
161
+ int nr_threads;
162
+};
163
+
164
+/*
165
+ * Start an IPC server instance in one or more background threads
166
+ * and return a handle to the pool.
167
+ *
168
+ * Returns 0 if the asynchronous server pool was started successfully.
169
+ * Returns -1 if not.
170
+ * Returns -2 if we could not startup because another server is using
171
+ * the socket or named pipe.
172
+ *
173
+ * When a client IPC message is received, the `application_cb` will be
174
+ * called (possibly on a random thread) to handle the message and
175
+ * optionally compose a reply message.
176
+ */
177
+int ipc_server_run_async(struct ipc_server_data **returned_server_data,
178
+ const char *path, const struct ipc_server_opts *opts,
179
+ ipc_server_application_cb *application_cb,
180
+ void *application_data);
181
+
182
+/*
183
+ * Gently signal the IPC server pool to shutdown. No new client
184
+ * connections will be accepted, but existing connections will be
185
+ * allowed to complete.
186
+ */
187
+int ipc_server_stop_async(struct ipc_server_data *server_data);
188
+
189
+/*
190
+ * Block the calling thread until all threads in the IPC server pool
191
+ * have completed and been joined.
192
+ */
193
+int ipc_server_await(struct ipc_server_data *server_data);
194
+
195
+/*
196
+ * Close and free all resource handles associated with the IPC server
197
+ * pool.
198
+ */
199
+void ipc_server_free(struct ipc_server_data *server_data);
200
+
201
+/*
202
+ * Run an IPC server instance and block the calling thread of the
203
+ * current process. It does not return until the IPC server has
204
+ * either shutdown or had an unrecoverable error.
205
+ *
206
+ * The IPC server handles incoming IPC messages from client processes
207
+ * and may use one or more background threads as necessary.
208
+ *
209
+ * Returns 0 after the server has completed successfully.
210
+ * Returns -1 if the server cannot be started.
211
+ * Returns -2 if we could not startup because another server is using
212
+ * the socket or named pipe.
213
+ *
214
+ * When a client IPC message is received, the `application_cb` will be
215
+ * called (possibly on a random thread) to handle the message and
216
+ * optionally compose a reply message.
217
+ *
218
+ * Note that `ipc_server_run()` is a synchronous wrapper around the
219
+ * above asynchronous routines. It effectively hides all of the
220
+ * server state and thread details from the caller and presents a
221
+ * simple synchronous interface.
222
+ */
223
+int ipc_server_run(const char *path, const struct ipc_server_opts *opts,
224
+ ipc_server_application_cb *application_cb,
225
+ void *application_data);
226
+
227
+#endif /* SUPPORTS_SIMPLE_IPC */
228
+#endif /* GIT_SIMPLE_IPC_H */