git

Форк
0
/
ipc-win32.c 
904 строки · 22.4 Кб
1
#include "git-compat-util.h"
2
#include "abspath.h"
3
#include "gettext.h"
4
#include "simple-ipc.h"
5
#include "strbuf.h"
6
#include "pkt-line.h"
7
#include "thread-utils.h"
8
#include "trace.h"
9
#include "trace2.h"
10
#include "accctrl.h"
11
#include "aclapi.h"
12

13
#ifndef SUPPORTS_SIMPLE_IPC
14
/*
15
 * This source file should only be compiled when Simple IPC is supported.
16
 * See the top-level Makefile.
17
 */
18
#error SUPPORTS_SIMPLE_IPC not defined
19
#endif
20

21
static int initialize_pipe_name(const char *path, wchar_t *wpath, size_t alloc)
22
{
23
	int off = 0;
24
	struct strbuf realpath = STRBUF_INIT;
25

26
	if (!strbuf_realpath(&realpath, path, 0))
27
		return -1;
28

29
	off = swprintf(wpath, alloc, L"\\\\.\\pipe\\");
30
	if (xutftowcs(wpath + off, realpath.buf, alloc - off) < 0)
31
		return -1;
32

33
	/* Handle drive prefix */
34
	if (wpath[off] && wpath[off + 1] == L':') {
35
		wpath[off + 1] = L'_';
36
		off += 2;
37
	}
38

39
	for (; wpath[off]; off++)
40
		if (wpath[off] == L'/')
41
			wpath[off] = L'\\';
42

43
	strbuf_release(&realpath);
44
	return 0;
45
}
46

47
static enum ipc_active_state get_active_state(wchar_t *pipe_path)
48
{
49
	if (WaitNamedPipeW(pipe_path, NMPWAIT_USE_DEFAULT_WAIT))
50
		return IPC_STATE__LISTENING;
51

52
	if (GetLastError() == ERROR_SEM_TIMEOUT)
53
		return IPC_STATE__NOT_LISTENING;
54

55
	if (GetLastError() == ERROR_FILE_NOT_FOUND)
56
		return IPC_STATE__PATH_NOT_FOUND;
57

58
	trace2_data_intmax("ipc-debug", NULL, "getstate/waitpipe/gle",
59
			   (intmax_t)GetLastError());
60

61
	return IPC_STATE__OTHER_ERROR;
62
}
63

64
enum ipc_active_state ipc_get_active_state(const char *path)
65
{
66
	wchar_t pipe_path[MAX_PATH];
67

68
	if (initialize_pipe_name(path, pipe_path, ARRAY_SIZE(pipe_path)) < 0)
69
		return IPC_STATE__INVALID_PATH;
70

71
	return get_active_state(pipe_path);
72
}
73

74
#define WAIT_STEP_MS (50)
75

76
static enum ipc_active_state connect_to_server(
77
	const wchar_t *wpath,
78
	DWORD timeout_ms,
79
	const struct ipc_client_connect_options *options,
80
	int *pfd)
81
{
82
	DWORD t_start_ms, t_waited_ms;
83
	DWORD step_ms;
84
	HANDLE hPipe = INVALID_HANDLE_VALUE;
85
	DWORD mode = PIPE_READMODE_BYTE;
86
	DWORD gle;
87

88
	*pfd = -1;
89

90
	for (;;) {
91
		hPipe = CreateFileW(wpath, GENERIC_READ | GENERIC_WRITE,
92
				    0, NULL, OPEN_EXISTING, 0, NULL);
93
		if (hPipe != INVALID_HANDLE_VALUE)
94
			break;
95

96
		gle = GetLastError();
97

98
		switch (gle) {
99
		case ERROR_FILE_NOT_FOUND:
100
			if (!options->wait_if_not_found)
101
				return IPC_STATE__PATH_NOT_FOUND;
102
			if (!timeout_ms)
103
				return IPC_STATE__PATH_NOT_FOUND;
104

105
			step_ms = (timeout_ms < WAIT_STEP_MS) ?
106
				timeout_ms : WAIT_STEP_MS;
107
			sleep_millisec(step_ms);
108

109
			timeout_ms -= step_ms;
110
			break; /* try again */
111

112
		case ERROR_PIPE_BUSY:
113
			if (!options->wait_if_busy)
114
				return IPC_STATE__NOT_LISTENING;
115
			if (!timeout_ms)
116
				return IPC_STATE__NOT_LISTENING;
117

118
			t_start_ms = (DWORD)(getnanotime() / 1000000);
119

120
			if (!WaitNamedPipeW(wpath, timeout_ms)) {
121
				DWORD gleWait = GetLastError();
122

123
				if (gleWait == ERROR_SEM_TIMEOUT)
124
					return IPC_STATE__NOT_LISTENING;
125

126
				trace2_data_intmax("ipc-debug", NULL,
127
						   "connect/waitpipe/gle",
128
						   (intmax_t)gleWait);
129

130
				return IPC_STATE__OTHER_ERROR;
131
			}
132

133
			/*
134
			 * A pipe server instance became available.
135
			 * Race other client processes to connect to
136
			 * it.
137
			 *
138
			 * But first decrement our overall timeout so
139
			 * that we don't starve if we keep losing the
140
			 * race.  But also guard against special
141
			 * NPMWAIT_ values (0 and -1).
142
			 */
143
			t_waited_ms = (DWORD)(getnanotime() / 1000000) - t_start_ms;
144
			if (t_waited_ms < timeout_ms)
145
				timeout_ms -= t_waited_ms;
146
			else
147
				timeout_ms = 1;
148
			break; /* try again */
149

150
		default:
151
			trace2_data_intmax("ipc-debug", NULL,
152
					   "connect/createfile/gle",
153
					   (intmax_t)gle);
154

155
			return IPC_STATE__OTHER_ERROR;
156
		}
157
	}
158

159
	if (!SetNamedPipeHandleState(hPipe, &mode, NULL, NULL)) {
160
		gle = GetLastError();
161
		trace2_data_intmax("ipc-debug", NULL,
162
				   "connect/setpipestate/gle",
163
				   (intmax_t)gle);
164

165
		CloseHandle(hPipe);
166
		return IPC_STATE__OTHER_ERROR;
167
	}
168

169
	*pfd = _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY);
170
	if (*pfd < 0) {
171
		gle = GetLastError();
172
		trace2_data_intmax("ipc-debug", NULL,
173
				   "connect/openosfhandle/gle",
174
				   (intmax_t)gle);
175

176
		CloseHandle(hPipe);
177
		return IPC_STATE__OTHER_ERROR;
178
	}
179

180
	/* fd now owns hPipe */
181

182
	return IPC_STATE__LISTENING;
183
}
184

185
/*
186
 * The default connection timeout for Windows clients.
187
 *
188
 * This is not currently part of the ipc_ API (nor the config settings)
189
 * because of differences between Windows and other platforms.
190
 *
191
 * This value was chosen at random.
192
 */
193
#define WINDOWS_CONNECTION_TIMEOUT_MS (30000)
194

195
enum ipc_active_state ipc_client_try_connect(
196
	const char *path,
197
	const struct ipc_client_connect_options *options,
198
	struct ipc_client_connection **p_connection)
199
{
200
	wchar_t wpath[MAX_PATH];
201
	enum ipc_active_state state = IPC_STATE__OTHER_ERROR;
202
	int fd = -1;
203

204
	*p_connection = NULL;
205

206
	trace2_region_enter("ipc-client", "try-connect", NULL);
207
	trace2_data_string("ipc-client", NULL, "try-connect/path", path);
208

209
	if (initialize_pipe_name(path, wpath, ARRAY_SIZE(wpath)) < 0)
210
		state = IPC_STATE__INVALID_PATH;
211
	else
212
		state = connect_to_server(wpath, WINDOWS_CONNECTION_TIMEOUT_MS,
213
					  options, &fd);
214

215
	trace2_data_intmax("ipc-client", NULL, "try-connect/state",
216
			   (intmax_t)state);
217
	trace2_region_leave("ipc-client", "try-connect", NULL);
218

219
	if (state == IPC_STATE__LISTENING) {
220
		(*p_connection) = xcalloc(1, sizeof(struct ipc_client_connection));
221
		(*p_connection)->fd = fd;
222
	}
223

224
	return state;
225
}
226

227
void ipc_client_close_connection(struct ipc_client_connection *connection)
228
{
229
	if (!connection)
230
		return;
231

232
	if (connection->fd != -1)
233
		close(connection->fd);
234

235
	free(connection);
236
}
237

238
int ipc_client_send_command_to_connection(
239
	struct ipc_client_connection *connection,
240
	const char *message, size_t message_len,
241
	struct strbuf *answer)
242
{
243
	int ret = 0;
244

245
	strbuf_setlen(answer, 0);
246

247
	trace2_region_enter("ipc-client", "send-command", NULL);
248

249
	if (write_packetized_from_buf_no_flush(message, message_len,
250
					       connection->fd) < 0 ||
251
	    packet_flush_gently(connection->fd) < 0) {
252
		ret = error(_("could not send IPC command"));
253
		goto done;
254
	}
255

256
	FlushFileBuffers((HANDLE)_get_osfhandle(connection->fd));
257

258
	if (read_packetized_to_strbuf(
259
		    connection->fd, answer,
260
		    PACKET_READ_GENTLE_ON_EOF | PACKET_READ_GENTLE_ON_READ_ERROR) < 0) {
261
		ret = error(_("could not read IPC response"));
262
		goto done;
263
	}
264

265
done:
266
	trace2_region_leave("ipc-client", "send-command", NULL);
267
	return ret;
268
}
269

270
int ipc_client_send_command(const char *path,
271
			    const struct ipc_client_connect_options *options,
272
			    const char *message, size_t message_len,
273
			    struct strbuf *response)
274
{
275
	int ret = -1;
276
	enum ipc_active_state state;
277
	struct ipc_client_connection *connection = NULL;
278

279
	state = ipc_client_try_connect(path, options, &connection);
280

281
	if (state != IPC_STATE__LISTENING)
282
		return ret;
283

284
	ret = ipc_client_send_command_to_connection(connection,
285
						    message, message_len,
286
						    response);
287

288
	ipc_client_close_connection(connection);
289

290
	return ret;
291
}
292

293
/*
294
 * Duplicate the given pipe handle and wrap it in a file descriptor so
295
 * that we can use pkt-line on it.
296
 */
297
static int dup_fd_from_pipe(const HANDLE pipe)
298
{
299
	HANDLE process = GetCurrentProcess();
300
	HANDLE handle;
301
	int fd;
302

303
	if (!DuplicateHandle(process, pipe, process, &handle, 0, FALSE,
304
			     DUPLICATE_SAME_ACCESS)) {
305
		errno = err_win_to_posix(GetLastError());
306
		return -1;
307
	}
308

309
	fd = _open_osfhandle((intptr_t)handle, O_RDWR|O_BINARY);
310
	if (fd < 0) {
311
		errno = err_win_to_posix(GetLastError());
312
		CloseHandle(handle);
313
		return -1;
314
	}
315

316
	/*
317
	 * `handle` is now owned by `fd` and will be automatically closed
318
	 * when the descriptor is closed.
319
	 */
320

321
	return fd;
322
}
323

324
/*
325
 * Magic numbers used to annotate callback instance data.
326
 * These are used to help guard against accidentally passing the
327
 * wrong instance data across multiple levels of callbacks (which
328
 * is easy to do if there are `void*` arguments).
329
 */
330
enum magic {
331
	MAGIC_SERVER_REPLY_DATA,
332
	MAGIC_SERVER_THREAD_DATA,
333
	MAGIC_SERVER_DATA,
334
};
335

336
struct ipc_server_reply_data {
337
	enum magic magic;
338
	int fd;
339
	struct ipc_server_thread_data *server_thread_data;
340
};
341

342
struct ipc_server_thread_data {
343
	enum magic magic;
344
	struct ipc_server_thread_data *next_thread;
345
	struct ipc_server_data *server_data;
346
	pthread_t pthread_id;
347
	HANDLE hPipe;
348
};
349

350
/*
351
 * On Windows, the conceptual "ipc-server" is implemented as a pool of
352
 * n idential/peer "server-thread" threads.  That is, there is no
353
 * hierarchy of threads; and therefore no controller thread managing
354
 * the pool.  Each thread has an independent handle to the named pipe,
355
 * receives incoming connections, processes the client, and re-uses
356
 * the pipe for the next client connection.
357
 *
358
 * Therefore, the "ipc-server" only needs to maintain a list of the
359
 * spawned threads for eventual "join" purposes.
360
 *
361
 * A single "stop-event" is visible to all of the server threads to
362
 * tell them to shutdown (when idle).
363
 */
364
struct ipc_server_data {
365
	enum magic magic;
366
	ipc_server_application_cb *application_cb;
367
	void *application_data;
368
	struct strbuf buf_path;
369
	wchar_t wpath[MAX_PATH];
370

371
	HANDLE hEventStopRequested;
372
	struct ipc_server_thread_data *thread_list;
373
	int is_stopped;
374
};
375

376
enum connect_result {
377
	CR_CONNECTED = 0,
378
	CR_CONNECT_PENDING,
379
	CR_CONNECT_ERROR,
380
	CR_WAIT_ERROR,
381
	CR_SHUTDOWN,
382
};
383

384
static enum connect_result queue_overlapped_connect(
385
	struct ipc_server_thread_data *server_thread_data,
386
	OVERLAPPED *lpo)
387
{
388
	if (ConnectNamedPipe(server_thread_data->hPipe, lpo))
389
		goto failed;
390

391
	switch (GetLastError()) {
392
	case ERROR_IO_PENDING:
393
		return CR_CONNECT_PENDING;
394

395
	case ERROR_PIPE_CONNECTED:
396
		SetEvent(lpo->hEvent);
397
		return CR_CONNECTED;
398

399
	default:
400
		break;
401
	}
402

403
failed:
404
	error(_("ConnectNamedPipe failed for '%s' (%lu)"),
405
	      server_thread_data->server_data->buf_path.buf,
406
	      GetLastError());
407
	return CR_CONNECT_ERROR;
408
}
409

410
/*
411
 * Use Windows Overlapped IO to wait for a connection or for our event
412
 * to be signalled.
413
 */
414
static enum connect_result wait_for_connection(
415
	struct ipc_server_thread_data *server_thread_data,
416
	OVERLAPPED *lpo)
417
{
418
	enum connect_result r;
419
	HANDLE waitHandles[2];
420
	DWORD dwWaitResult;
421

422
	r = queue_overlapped_connect(server_thread_data, lpo);
423
	if (r != CR_CONNECT_PENDING)
424
		return r;
425

426
	waitHandles[0] = server_thread_data->server_data->hEventStopRequested;
427
	waitHandles[1] = lpo->hEvent;
428

429
	dwWaitResult = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
430
	switch (dwWaitResult) {
431
	case WAIT_OBJECT_0 + 0:
432
		return CR_SHUTDOWN;
433

434
	case WAIT_OBJECT_0 + 1:
435
		ResetEvent(lpo->hEvent);
436
		return CR_CONNECTED;
437

438
	default:
439
		return CR_WAIT_ERROR;
440
	}
441
}
442

443
/*
444
 * Forward declare our reply callback function so that any compiler
445
 * errors are reported when we actually define the function (in addition
446
 * to any errors reported when we try to pass this callback function as
447
 * a parameter in a function call).  The former are easier to understand.
448
 */
449
static ipc_server_reply_cb do_io_reply_callback;
450

451
/*
452
 * Relay application's response message to the client process.
453
 * (We do not flush at this point because we allow the caller
454
 * to chunk data to the client thru us.)
455
 */
456
static int do_io_reply_callback(struct ipc_server_reply_data *reply_data,
457
		       const char *response, size_t response_len)
458
{
459
	if (reply_data->magic != MAGIC_SERVER_REPLY_DATA)
460
		BUG("reply_cb called with wrong instance data");
461

462
	return write_packetized_from_buf_no_flush(response, response_len,
463
						  reply_data->fd);
464
}
465

466
/*
467
 * Receive the request/command from the client and pass it to the
468
 * registered request-callback.  The request-callback will compose
469
 * a response and call our reply-callback to send it to the client.
470
 *
471
 * Simple-IPC only contains one round trip, so we flush and close
472
 * here after the response.
473
 */
474
static int do_io(struct ipc_server_thread_data *server_thread_data)
475
{
476
	struct strbuf buf = STRBUF_INIT;
477
	struct ipc_server_reply_data reply_data;
478
	int ret = 0;
479

480
	reply_data.magic = MAGIC_SERVER_REPLY_DATA;
481
	reply_data.server_thread_data = server_thread_data;
482

483
	reply_data.fd = dup_fd_from_pipe(server_thread_data->hPipe);
484
	if (reply_data.fd < 0)
485
		return error(_("could not create fd from pipe for '%s'"),
486
			     server_thread_data->server_data->buf_path.buf);
487

488
	ret = read_packetized_to_strbuf(
489
		reply_data.fd, &buf,
490
		PACKET_READ_GENTLE_ON_EOF | PACKET_READ_GENTLE_ON_READ_ERROR);
491
	if (ret >= 0) {
492
		ret = server_thread_data->server_data->application_cb(
493
			server_thread_data->server_data->application_data,
494
			buf.buf, buf.len, do_io_reply_callback, &reply_data);
495

496
		packet_flush_gently(reply_data.fd);
497

498
		FlushFileBuffers((HANDLE)_get_osfhandle((reply_data.fd)));
499
	}
500
	else {
501
		/*
502
		 * The client probably disconnected/shutdown before it
503
		 * could send a well-formed message.  Ignore it.
504
		 */
505
	}
506

507
	strbuf_release(&buf);
508
	close(reply_data.fd);
509

510
	return ret;
511
}
512

513
/*
514
 * Handle IPC request and response with this connected client.  And reset
515
 * the pipe to prepare for the next client.
516
 */
517
static int use_connection(struct ipc_server_thread_data *server_thread_data)
518
{
519
	int ret;
520

521
	ret = do_io(server_thread_data);
522

523
	FlushFileBuffers(server_thread_data->hPipe);
524
	DisconnectNamedPipe(server_thread_data->hPipe);
525

526
	return ret;
527
}
528

529
/*
530
 * Thread proc for an IPC server worker thread.  It handles a series of
531
 * connections from clients.  It cleans and reuses the hPipe between each
532
 * client.
533
 */
534
static void *server_thread_proc(void *_server_thread_data)
535
{
536
	struct ipc_server_thread_data *server_thread_data = _server_thread_data;
537
	HANDLE hEventConnected = INVALID_HANDLE_VALUE;
538
	OVERLAPPED oConnect;
539
	enum connect_result cr;
540
	int ret;
541

542
	assert(server_thread_data->hPipe != INVALID_HANDLE_VALUE);
543

544
	trace2_thread_start("ipc-server");
545
	trace2_data_string("ipc-server", NULL, "pipe",
546
			   server_thread_data->server_data->buf_path.buf);
547

548
	hEventConnected = CreateEventW(NULL, TRUE, FALSE, NULL);
549

550
	memset(&oConnect, 0, sizeof(oConnect));
551
	oConnect.hEvent = hEventConnected;
552

553
	for (;;) {
554
		cr = wait_for_connection(server_thread_data, &oConnect);
555

556
		switch (cr) {
557
		case CR_SHUTDOWN:
558
			goto finished;
559

560
		case CR_CONNECTED:
561
			ret = use_connection(server_thread_data);
562
			if (ret == SIMPLE_IPC_QUIT) {
563
				ipc_server_stop_async(
564
					server_thread_data->server_data);
565
				goto finished;
566
			}
567
			if (ret > 0) {
568
				/*
569
				 * Ignore (transient) IO errors with this
570
				 * client and reset for the next client.
571
				 */
572
			}
573
			break;
574

575
		case CR_CONNECT_PENDING:
576
			/* By construction, this should not happen. */
577
			BUG("ipc-server[%s]: unexpeced CR_CONNECT_PENDING",
578
			    server_thread_data->server_data->buf_path.buf);
579

580
		case CR_CONNECT_ERROR:
581
		case CR_WAIT_ERROR:
582
			/*
583
			 * Ignore these theoretical errors.
584
			 */
585
			DisconnectNamedPipe(server_thread_data->hPipe);
586
			break;
587

588
		default:
589
			BUG("unandled case after wait_for_connection");
590
		}
591
	}
592

593
finished:
594
	CloseHandle(server_thread_data->hPipe);
595
	CloseHandle(hEventConnected);
596

597
	trace2_thread_exit();
598
	return NULL;
599
}
600

601
/*
602
 * We need to build a Windows "SECURITY_ATTRIBUTES" object and use it
603
 * to apply an ACL when we create the initial instance of the Named
604
 * Pipe.  The construction is somewhat involved and consists of
605
 * several sequential steps and intermediate objects.
606
 *
607
 * We use this structure to hold these intermediate pointers so that
608
 * we can free them as a group.  (It is unclear from the docs whether
609
 * some of these intermediate pointers can be freed before we are
610
 * finished using the "lpSA" member.)
611
 */
612
struct my_sa_data
613
{
614
	PSID pEveryoneSID;
615
	PACL pACL;
616
	PSECURITY_DESCRIPTOR pSD;
617
	LPSECURITY_ATTRIBUTES lpSA;
618
};
619

620
static void init_sa(struct my_sa_data *d)
621
{
622
	memset(d, 0, sizeof(*d));
623
}
624

625
static void release_sa(struct my_sa_data *d)
626
{
627
	if (d->pEveryoneSID)
628
		FreeSid(d->pEveryoneSID);
629
	if (d->pACL)
630
		LocalFree(d->pACL);
631
	if (d->pSD)
632
		LocalFree(d->pSD);
633
	if (d->lpSA)
634
		LocalFree(d->lpSA);
635

636
	memset(d, 0, sizeof(*d));
637
}
638

639
/*
640
 * Create SECURITY_ATTRIBUTES to apply to the initial named pipe.  The
641
 * creator of the first server instance gets to set the ACLs on it.
642
 *
643
 * We allow the well-known group `EVERYONE` to have read+write access
644
 * to the named pipe so that clients can send queries to the daemon
645
 * and receive the response.
646
 *
647
 * Normally, this is not necessary since the daemon is usually
648
 * automatically started by a foreground command like `git status`,
649
 * but in those cases where an elevated Git command started the daemon
650
 * (such that the daemon itself runs with elevation), we need to add
651
 * the ACL so that non-elevated commands can write to it.
652
 *
653
 * The following document was helpful:
654
 * https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
655
 *
656
 * Returns d->lpSA set to a SA or NULL.
657
 */
658
static LPSECURITY_ATTRIBUTES get_sa(struct my_sa_data *d)
659
{
660
	SID_IDENTIFIER_AUTHORITY sid_auth_world = SECURITY_WORLD_SID_AUTHORITY;
661
#define NR_EA (1)
662
	EXPLICIT_ACCESS ea[NR_EA];
663
	DWORD dwResult;
664

665
	if (!AllocateAndInitializeSid(&sid_auth_world, 1,
666
				      SECURITY_WORLD_RID, 0,0,0,0,0,0,0,
667
				      &d->pEveryoneSID)) {
668
		DWORD gle = GetLastError();
669
		trace2_data_intmax("ipc-debug", NULL, "alloc-world-sid/gle",
670
				   (intmax_t)gle);
671
		goto fail;
672
	}
673

674
	memset(ea, 0, NR_EA * sizeof(EXPLICIT_ACCESS));
675

676
	ea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
677
	ea[0].grfAccessMode = SET_ACCESS;
678
	ea[0].grfInheritance = NO_INHERITANCE;
679
	ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
680
	ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
681
	ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
682
	ea[0].Trustee.ptstrName = (LPTSTR)d->pEveryoneSID;
683

684
	dwResult = SetEntriesInAcl(NR_EA, ea, NULL, &d->pACL);
685
	if (dwResult != ERROR_SUCCESS) {
686
		DWORD gle = GetLastError();
687
		trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/gle",
688
				   (intmax_t)gle);
689
		trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/dw",
690
				   (intmax_t)dwResult);
691
		goto fail;
692
	}
693

694
	d->pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(
695
		LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
696
	if (!InitializeSecurityDescriptor(d->pSD, SECURITY_DESCRIPTOR_REVISION)) {
697
		DWORD gle = GetLastError();
698
		trace2_data_intmax("ipc-debug", NULL, "init-sd/gle", (intmax_t)gle);
699
		goto fail;
700
	}
701

702
	if (!SetSecurityDescriptorDacl(d->pSD, TRUE, d->pACL, FALSE)) {
703
		DWORD gle = GetLastError();
704
		trace2_data_intmax("ipc-debug", NULL, "set-sd-dacl/gle", (intmax_t)gle);
705
		goto fail;
706
	}
707

708
	d->lpSA = (LPSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
709
	d->lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
710
	d->lpSA->lpSecurityDescriptor = d->pSD;
711
	d->lpSA->bInheritHandle = FALSE;
712

713
	return d->lpSA;
714

715
fail:
716
	release_sa(d);
717
	return NULL;
718
}
719

720
static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
721
{
722
	HANDLE hPipe;
723
	DWORD dwOpenMode, dwPipeMode;
724
	struct my_sa_data my_sa_data;
725

726
	init_sa(&my_sa_data);
727

728
	dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
729
		FILE_FLAG_OVERLAPPED;
730

731
	dwPipeMode = PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT |
732
		PIPE_REJECT_REMOTE_CLIENTS;
733

734
	if (is_first) {
735
		dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
736

737
		/*
738
		 * On Windows, the first server pipe instance gets to
739
		 * set the ACL / Security Attributes on the named
740
		 * pipe; subsequent instances inherit and cannot
741
		 * change them.
742
		 */
743
		get_sa(&my_sa_data);
744
	}
745

746
	hPipe = CreateNamedPipeW(wpath, dwOpenMode, dwPipeMode,
747
				 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0,
748
				 my_sa_data.lpSA);
749

750
	release_sa(&my_sa_data);
751

752
	return hPipe;
753
}
754

755
int ipc_server_run_async(struct ipc_server_data **returned_server_data,
756
			 const char *path, const struct ipc_server_opts *opts,
757
			 ipc_server_application_cb *application_cb,
758
			 void *application_data)
759
{
760
	struct ipc_server_data *server_data;
761
	wchar_t wpath[MAX_PATH];
762
	HANDLE hPipeFirst = INVALID_HANDLE_VALUE;
763
	int k;
764
	int ret = 0;
765
	int nr_threads = opts->nr_threads;
766

767
	*returned_server_data = NULL;
768

769
	ret = initialize_pipe_name(path, wpath, ARRAY_SIZE(wpath));
770
	if (ret < 0) {
771
		errno = EINVAL;
772
		return -1;
773
	}
774

775
	hPipeFirst = create_new_pipe(wpath, 1);
776
	if (hPipeFirst == INVALID_HANDLE_VALUE) {
777
		errno = EADDRINUSE;
778
		return -2;
779
	}
780

781
	server_data = xcalloc(1, sizeof(*server_data));
782
	server_data->magic = MAGIC_SERVER_DATA;
783
	server_data->application_cb = application_cb;
784
	server_data->application_data = application_data;
785
	server_data->hEventStopRequested = CreateEvent(NULL, TRUE, FALSE, NULL);
786
	strbuf_init(&server_data->buf_path, 0);
787
	strbuf_addstr(&server_data->buf_path, path);
788
	wcscpy(server_data->wpath, wpath);
789

790
	if (nr_threads < 1)
791
		nr_threads = 1;
792

793
	for (k = 0; k < nr_threads; k++) {
794
		struct ipc_server_thread_data *std;
795

796
		std = xcalloc(1, sizeof(*std));
797
		std->magic = MAGIC_SERVER_THREAD_DATA;
798
		std->server_data = server_data;
799
		std->hPipe = INVALID_HANDLE_VALUE;
800

801
		std->hPipe = (k == 0)
802
			? hPipeFirst
803
			: create_new_pipe(server_data->wpath, 0);
804

805
		if (std->hPipe == INVALID_HANDLE_VALUE) {
806
			/*
807
			 * If we've reached a pipe instance limit for
808
			 * this path, just use fewer threads.
809
			 */
810
			free(std);
811
			break;
812
		}
813

814
		if (pthread_create(&std->pthread_id, NULL,
815
				   server_thread_proc, std)) {
816
			/*
817
			 * Likewise, if we're out of threads, just use
818
			 * fewer threads than requested.
819
			 *
820
			 * However, we just give up if we can't even get
821
			 * one thread.  This should not happen.
822
			 */
823
			if (k == 0)
824
				die(_("could not start thread[0] for '%s'"),
825
				    path);
826

827
			CloseHandle(std->hPipe);
828
			free(std);
829
			break;
830
		}
831

832
		std->next_thread = server_data->thread_list;
833
		server_data->thread_list = std;
834
	}
835

836
	*returned_server_data = server_data;
837
	return 0;
838
}
839

840
int ipc_server_stop_async(struct ipc_server_data *server_data)
841
{
842
	if (!server_data)
843
		return 0;
844

845
	/*
846
	 * Gently tell all of the ipc_server threads to shutdown.
847
	 * This will be seen the next time they are idle (and waiting
848
	 * for a connection).
849
	 *
850
	 * We DO NOT attempt to force them to drop an active connection.
851
	 */
852
	SetEvent(server_data->hEventStopRequested);
853
	return 0;
854
}
855

856
int ipc_server_await(struct ipc_server_data *server_data)
857
{
858
	DWORD dwWaitResult;
859

860
	if (!server_data)
861
		return 0;
862

863
	dwWaitResult = WaitForSingleObject(server_data->hEventStopRequested, INFINITE);
864
	if (dwWaitResult != WAIT_OBJECT_0)
865
		return error(_("wait for hEvent failed for '%s'"),
866
			     server_data->buf_path.buf);
867

868
	while (server_data->thread_list) {
869
		struct ipc_server_thread_data *std = server_data->thread_list;
870

871
		pthread_join(std->pthread_id, NULL);
872

873
		server_data->thread_list = std->next_thread;
874
		free(std);
875
	}
876

877
	server_data->is_stopped = 1;
878

879
	return 0;
880
}
881

882
void ipc_server_free(struct ipc_server_data *server_data)
883
{
884
	if (!server_data)
885
		return;
886

887
	if (!server_data->is_stopped)
888
		BUG("cannot free ipc-server while running for '%s'",
889
		    server_data->buf_path.buf);
890

891
	strbuf_release(&server_data->buf_path);
892

893
	if (server_data->hEventStopRequested != INVALID_HANDLE_VALUE)
894
		CloseHandle(server_data->hEventStopRequested);
895

896
	while (server_data->thread_list) {
897
		struct ipc_server_thread_data *std = server_data->thread_list;
898

899
		server_data->thread_list = std->next_thread;
900
		free(std);
901
	}
902

903
	free(server_data);
904
}
905

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.